1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
62 /* ChanServ database */
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans"
103 #define KEY_MAX "max"
104 #define KEY_NOTES "notes"
105 #define KEY_TOPIC_MASK "topic_mask"
106 #define KEY_OWNER_TRANSFER "owner_transfer"
109 #define KEY_LEVEL "level"
110 #define KEY_INFO "info"
111 #define KEY_SEEN "seen"
114 #define KEY_OWNER "owner"
115 #define KEY_REASON "reason"
116 #define KEY_SET "set"
117 #define KEY_DURATION "duration"
118 #define KEY_EXPIRES "expires"
119 #define KEY_TRIGGERED "triggered"
121 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
122 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
123 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
125 /* Administrative messages */
126 static const struct message_entry msgtab[] = {
127 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
129 /* Channel registration */
130 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
131 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
132 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
133 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
134 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
135 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
137 /* Do-not-register channels */
138 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
139 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
140 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
141 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
142 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
143 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
144 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
145 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
146 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
147 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
148 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
149 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
150 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
151 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
153 /* Channel unregistration */
154 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
155 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
156 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
157 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
160 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
161 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
163 /* Channel merging */
164 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
165 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
166 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
167 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
168 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
170 /* Handle unregistration */
171 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
174 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
175 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
176 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
177 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
178 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
179 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
180 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
181 { "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." },
182 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
183 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
184 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
185 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
186 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
187 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
189 /* Removing yourself from a channel. */
190 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
191 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
192 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
194 /* User management */
195 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
196 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
197 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
198 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
199 { "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." },
200 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
201 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
202 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
204 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
205 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
206 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
207 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
208 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
209 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
210 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
213 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
214 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
215 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
216 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
217 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
218 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
219 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
220 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
221 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
222 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
223 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
224 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
225 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
226 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
227 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
228 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
230 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
232 /* Channel management */
233 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
234 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
235 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
237 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
238 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
239 { "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" },
240 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
241 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
242 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
243 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
245 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
246 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
247 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
248 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
249 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
250 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
251 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
252 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
253 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
254 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
255 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
256 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
257 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
258 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
259 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
260 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
261 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
262 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
263 { "CSMSG_SET_MODES", "$bModes $b %s" },
264 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
265 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
266 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
267 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
268 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
269 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
270 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
271 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
272 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
273 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
274 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
275 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
276 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
277 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
278 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
279 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
280 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
281 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
282 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
283 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
284 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
285 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
286 { "CSMSG_USET_INFO", "$bInfo $b %s" },
288 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
289 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
290 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
291 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
292 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
293 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
294 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
295 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
296 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
297 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
298 { "CSMSG_PROTECT_NONE", "No users will be protected." },
299 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
300 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
301 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
302 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
303 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
304 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
305 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
306 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
307 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
308 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
309 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
310 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
312 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
313 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
314 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
315 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
316 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
317 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
318 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
319 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
321 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
322 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
323 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
325 /* Channel userlist */
326 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
327 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
328 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
329 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
331 /* Channel note list */
332 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
333 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
334 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
335 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
336 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
337 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
338 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
339 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
340 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
341 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
342 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
343 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
344 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
345 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
346 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
348 /* Channel [un]suspension */
349 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
350 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
351 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
352 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
353 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
354 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
355 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
357 /* Access information */
358 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
359 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
360 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
361 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
362 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
363 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
364 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
365 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
366 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
367 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
368 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
369 { "CSMSG_UC_H_TITLE", "network helper" },
370 { "CSMSG_LC_H_TITLE", "support helper" },
371 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
373 /* Seen information */
374 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
375 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
376 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
377 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
379 /* Names information */
380 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
381 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
383 /* Channel information */
384 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
385 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
386 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
387 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
388 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
389 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
390 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
391 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
392 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
393 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
394 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
395 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
400 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
401 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
402 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
403 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
404 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
406 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
407 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
408 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
409 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
410 { "CSMSG_PEEK_OPS", "$bOps:$b" },
411 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
413 /* Network information */
414 { "CSMSG_NETWORK_INFO", "Network Information:" },
415 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
416 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
417 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
418 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
419 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
420 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
421 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
422 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
425 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
426 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
427 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
429 /* Channel searches */
430 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
431 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
432 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
433 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
435 /* Channel configuration */
436 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
437 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
438 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
439 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
442 { "CSMSG_USER_OPTIONS", "User Options:" },
443 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
446 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
447 { "CSMSG_PING_RESPONSE", "Pong!" },
448 { "CSMSG_WUT_RESPONSE", "wut" },
449 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
450 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
451 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
452 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
453 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
454 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
455 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
458 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
462 /* eject_user and unban_user flags */
463 #define ACTION_KICK 0x0001
464 #define ACTION_BAN 0x0002
465 #define ACTION_ADD_BAN 0x0004
466 #define ACTION_ADD_TIMED_BAN 0x0008
467 #define ACTION_UNBAN 0x0010
468 #define ACTION_DEL_BAN 0x0020
470 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
471 #define MODELEN 40 + KEYLEN
475 #define CSFUNC_ARGS user, channel, argc, argv, cmd
477 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
478 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
479 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
480 reply("MSG_MISSING_PARAMS", argv[0]); \
484 DECLARE_LIST(dnrList, struct do_not_register *);
485 DEFINE_LIST(dnrList, struct do_not_register *)
487 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
489 struct userNode *chanserv;
492 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
493 static struct log_type *CS_LOG;
497 struct channelList support_channels;
498 struct mod_chanmode default_modes;
500 unsigned long db_backup_frequency;
501 unsigned long channel_expire_frequency;
502 unsigned long dnr_expire_frequency;
504 unsigned long invited_timeout;
506 unsigned long info_delay;
507 unsigned long adjust_delay;
508 unsigned long channel_expire_delay;
509 unsigned int nodelete_level;
511 unsigned int adjust_threshold;
512 int join_flood_threshold;
514 unsigned int greeting_length;
515 unsigned int refresh_period;
516 unsigned int giveownership_period;
518 unsigned int max_owned;
519 unsigned int max_chan_users;
520 unsigned int max_chan_bans;
521 unsigned int max_userinfo_length;
523 struct string_list *set_shows;
524 struct string_list *eightball;
525 struct string_list *old_ban_names;
527 const char *ctcp_short_ban_duration;
528 const char *ctcp_long_ban_duration;
530 const char *irc_operator_epithet;
531 const char *network_helper_epithet;
532 const char *support_helper_epithet;
537 struct userNode *user;
538 struct userNode *bot;
539 struct chanNode *channel;
541 unsigned short lowest;
542 unsigned short highest;
543 struct userData **users;
544 struct helpfile_table table;
549 struct userNode *user;
550 struct chanNode *chan;
553 enum note_access_type
555 NOTE_SET_CHANNEL_ACCESS,
556 NOTE_SET_CHANNEL_SETTER,
560 enum note_visible_type
563 NOTE_VIS_CHANNEL_USERS,
569 enum note_access_type set_access_type;
571 unsigned int min_opserv;
572 unsigned short min_ulevel;
574 enum note_visible_type visible_type;
575 unsigned int max_length;
582 struct note_type *type;
583 char setter[NICKSERV_HANDLE_LEN+1];
587 static unsigned int registered_channels;
588 static unsigned int banCount;
590 static const struct {
593 unsigned short level;
596 { "peon", "Peon", UL_PEON, '+' },
597 { "op", "Op", UL_OP, '@' },
598 { "master", "Master", UL_MASTER, '%' },
599 { "coowner", "Coowner", UL_COOWNER, '*' },
600 { "owner", "Owner", UL_OWNER, '!' },
601 { "helper", "BUG:", UL_HELPER, 'X' }
604 static const struct {
607 unsigned short default_value;
608 unsigned int old_idx;
609 unsigned int old_flag;
610 unsigned short flag_value;
612 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
613 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
614 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
615 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
616 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
617 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
618 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
619 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
620 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
621 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
622 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
625 struct charOptionValues {
628 } protectValues[] = {
629 { 'a', "CSMSG_PROTECT_ALL" },
630 { 'e', "CSMSG_PROTECT_EQUAL" },
631 { 'l', "CSMSG_PROTECT_LOWER" },
632 { 'n', "CSMSG_PROTECT_NONE" }
634 { 'd', "CSMSG_TOYS_DISABLED" },
635 { 'n', "CSMSG_TOYS_PRIVATE" },
636 { 'p', "CSMSG_TOYS_PUBLIC" }
637 }, topicRefreshValues[] = {
638 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
639 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
640 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
641 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
642 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
643 }, ctcpReactionValues[] = {
644 { 'k', "CSMSG_CTCPREACTION_KICK" },
645 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
646 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
647 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
650 static const struct {
654 unsigned int old_idx;
656 struct charOptionValues *values;
658 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
659 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
660 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
661 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
664 struct userData *helperList;
665 struct chanData *channelList;
666 static struct module *chanserv_module;
667 static unsigned int userCount;
669 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
670 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
673 user_level_from_name(const char *name, unsigned short clamp_level)
675 unsigned int level = 0, ii;
677 level = strtoul(name, NULL, 10);
678 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
679 if(!irccasecmp(name, accessLevels[ii].name))
680 level = accessLevels[ii].level;
681 if(level > clamp_level)
687 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
690 *minl = strtoul(arg, &sep, 10);
698 *maxl = strtoul(sep+1, &sep, 10);
706 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
708 struct userData *uData, **head;
710 if(!channel || !handle)
713 if(override && HANDLE_FLAGGED(handle, HELPING)
714 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
716 for(uData = helperList;
717 uData && uData->handle != handle;
718 uData = uData->next);
722 uData = calloc(1, sizeof(struct userData));
723 uData->handle = handle;
725 uData->access = UL_HELPER;
731 uData->next = helperList;
733 helperList->prev = uData;
741 for(uData = channel->users; uData; uData = uData->next)
742 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
745 head = &(channel->users);
748 if(uData && (uData != *head))
750 /* Shuffle the user to the head of whatever list he was in. */
752 uData->next->prev = uData->prev;
754 uData->prev->next = uData->next;
760 (**head).prev = uData;
767 /* Returns non-zero if user has at least the minimum access.
768 * exempt_owner is set when handling !set, so the owner can set things
771 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
773 struct userData *uData;
774 struct chanData *cData = channel->channel_info;
775 unsigned short minimum = cData->lvlOpts[opt];
778 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
781 if(minimum <= uData->access)
783 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
788 /* Scan for other users authenticated to the same handle
789 still in the channel. If so, keep them listed as present.
791 user is optional, if not null, it skips checking that userNode
792 (for the handle_part function) */
794 scan_user_presence(struct userData *uData, struct userNode *user)
798 if(IsSuspended(uData->channel)
799 || IsUserSuspended(uData)
800 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
812 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
814 unsigned int eflags, argc;
816 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
818 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
819 if(!channel->channel_info
820 || IsSuspended(channel->channel_info)
822 || !ircncasecmp(text, "ACTION ", 7))
824 /* Figure out the minimum level needed to CTCP the channel */
825 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
827 /* We need to enforce against them; do so. */
829 argv[0] = (char*)text;
830 argv[1] = user->nick;
832 if(GetUserMode(channel, user))
833 eflags |= ACTION_KICK;
834 switch(channel->channel_info->chOpts[chCTCPReaction]) {
835 default: case 'k': /* just do the kick */ break;
837 eflags |= ACTION_BAN;
840 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
841 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
844 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
845 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
848 argv[argc++] = bad_ctcp_reason;
849 eject_user(chanserv, channel, argc, argv, NULL, eflags);
853 chanserv_create_note_type(const char *name)
855 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
856 strcpy(ntype->name, name);
858 dict_insert(note_types, ntype->name, ntype);
863 chanserv_deref_note_type(void *data)
865 struct note_type *ntype = data;
867 if(--ntype->refs > 0)
873 chanserv_flush_note_type(struct note_type *ntype)
875 struct chanData *cData;
876 for(cData = channelList; cData; cData = cData->next)
877 dict_remove(cData->notes, ntype->name);
881 chanserv_truncate_notes(struct note_type *ntype)
883 struct chanData *cData;
885 unsigned int size = sizeof(*note) + ntype->max_length;
887 for(cData = channelList; cData; cData = cData->next) {
888 note = dict_find(cData->notes, ntype->name, NULL);
891 if(strlen(note->note) <= ntype->max_length)
893 dict_remove2(cData->notes, ntype->name, 1);
894 note = realloc(note, size);
895 note->note[ntype->max_length] = 0;
896 dict_insert(cData->notes, ntype->name, note);
900 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
903 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
906 unsigned int len = strlen(text);
908 if(len > type->max_length) len = type->max_length;
909 note = calloc(1, sizeof(*note) + len);
911 strncpy(note->setter, setter, sizeof(note->setter)-1);
912 memcpy(note->note, text, len);
914 dict_insert(channel->notes, type->name, note);
920 chanserv_free_note(void *data)
922 struct note *note = data;
924 chanserv_deref_note_type(note->type);
925 assert(note->type->refs > 0); /* must use delnote to remove the type */
929 static MODCMD_FUNC(cmd_createnote) {
930 struct note_type *ntype;
931 unsigned int arg = 1, existed = 0, max_length;
933 if((ntype = dict_find(note_types, argv[1], NULL)))
936 ntype = chanserv_create_note_type(argv[arg]);
937 if(!irccasecmp(argv[++arg], "privileged"))
940 ntype->set_access_type = NOTE_SET_PRIVILEGED;
941 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
943 else if(!irccasecmp(argv[arg], "channel"))
945 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
948 reply("CSMSG_INVALID_ACCESS", argv[arg]);
951 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
952 ntype->set_access.min_ulevel = ulvl;
954 else if(!irccasecmp(argv[arg], "setter"))
956 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
960 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
964 if(!irccasecmp(argv[++arg], "privileged"))
965 ntype->visible_type = NOTE_VIS_PRIVILEGED;
966 else if(!irccasecmp(argv[arg], "channel_users"))
967 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
968 else if(!irccasecmp(argv[arg], "all"))
969 ntype->visible_type = NOTE_VIS_ALL;
971 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
975 if((arg+1) >= argc) {
976 reply("MSG_MISSING_PARAMS", argv[0]);
979 max_length = strtoul(argv[++arg], NULL, 0);
980 if(max_length < 20 || max_length > 450)
982 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
985 if(existed && (max_length < ntype->max_length))
987 ntype->max_length = max_length;
988 chanserv_truncate_notes(ntype);
990 ntype->max_length = max_length;
993 reply("CSMSG_NOTE_MODIFIED", ntype->name);
995 reply("CSMSG_NOTE_CREATED", ntype->name);
1000 dict_remove(note_types, ntype->name);
1004 static MODCMD_FUNC(cmd_removenote) {
1005 struct note_type *ntype;
1008 ntype = dict_find(note_types, argv[1], NULL);
1009 force = (argc > 2) && !irccasecmp(argv[2], "force");
1012 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1019 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1022 chanserv_flush_note_type(ntype);
1024 dict_remove(note_types, argv[1]);
1025 reply("CSMSG_NOTE_DELETED", argv[1]);
1030 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1034 if(orig->modes_set & change->modes_clear)
1036 if(orig->modes_clear & change->modes_set)
1038 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1039 && strcmp(orig->new_key, change->new_key))
1041 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1042 && (orig->new_limit != change->new_limit))
1047 static char max_length_text[MAXLEN+1][16];
1049 static struct helpfile_expansion
1050 chanserv_expand_variable(const char *variable)
1052 struct helpfile_expansion exp;
1054 if(!irccasecmp(variable, "notes"))
1057 exp.type = HF_TABLE;
1058 exp.value.table.length = 1;
1059 exp.value.table.width = 3;
1060 exp.value.table.flags = 0;
1061 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1062 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1063 exp.value.table.contents[0][0] = "Note Type";
1064 exp.value.table.contents[0][1] = "Visibility";
1065 exp.value.table.contents[0][2] = "Max Length";
1066 for(it=dict_first(note_types); it; it=iter_next(it))
1068 struct note_type *ntype = iter_data(it);
1071 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1072 row = exp.value.table.length++;
1073 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1074 exp.value.table.contents[row][0] = ntype->name;
1075 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1076 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1078 if(!max_length_text[ntype->max_length][0])
1079 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1080 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1085 exp.type = HF_STRING;
1086 exp.value.str = NULL;
1090 static struct chanData*
1091 register_channel(struct chanNode *cNode, char *registrar)
1093 struct chanData *channel;
1094 enum levelOption lvlOpt;
1095 enum charOption chOpt;
1097 channel = calloc(1, sizeof(struct chanData));
1099 channel->notes = dict_new();
1100 dict_set_free_data(channel->notes, chanserv_free_note);
1102 channel->registrar = strdup(registrar);
1103 channel->registered = now;
1104 channel->visited = now;
1105 channel->limitAdjusted = now;
1106 channel->ownerTransfer = now;
1107 channel->flags = CHANNEL_DEFAULT_FLAGS;
1108 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1109 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1110 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1111 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1113 channel->prev = NULL;
1114 channel->next = channelList;
1117 channelList->prev = channel;
1118 channelList = channel;
1119 registered_channels++;
1121 channel->channel = cNode;
1123 cNode->channel_info = channel;
1128 static struct userData*
1129 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1131 struct userData *ud;
1133 if(access_level > UL_OWNER)
1136 ud = calloc(1, sizeof(*ud));
1137 ud->channel = channel;
1138 ud->handle = handle;
1140 ud->access = access_level;
1141 ud->info = info ? strdup(info) : NULL;
1144 ud->next = channel->users;
1146 channel->users->prev = ud;
1147 channel->users = ud;
1149 channel->userCount++;
1153 ud->u_next = ud->handle->channels;
1155 ud->u_next->u_prev = ud;
1156 ud->handle->channels = ud;
1161 static void unregister_channel(struct chanData *channel, const char *reason);
1164 del_channel_user(struct userData *user, int do_gc)
1166 struct chanData *channel = user->channel;
1168 channel->userCount--;
1172 user->prev->next = user->next;
1174 channel->users = user->next;
1176 user->next->prev = user->prev;
1179 user->u_prev->u_next = user->u_next;
1181 user->handle->channels = user->u_next;
1183 user->u_next->u_prev = user->u_prev;
1187 if(do_gc && !channel->users && !IsProtected(channel)) {
1188 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1189 unregister_channel(channel, "lost all users.");
1193 static void expire_ban(void *data);
1196 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1199 unsigned int ii, l1, l2;
1204 bd = malloc(sizeof(struct banData));
1206 bd->channel = channel;
1208 bd->triggered = triggered;
1209 bd->expires = expires;
1211 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1213 extern const char *hidden_host_suffix;
1214 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1218 l2 = strlen(old_name);
1221 if(irccasecmp(mask + l1 - l2, old_name))
1223 new_mask = alloca(MAXLEN);
1224 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1227 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1229 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1230 bd->reason = strdup(reason);
1233 timeq_add(expires, expire_ban, bd);
1236 bd->next = channel->bans;
1238 channel->bans->prev = bd;
1240 channel->banCount++;
1247 del_channel_ban(struct banData *ban)
1249 ban->channel->banCount--;
1253 ban->prev->next = ban->next;
1255 ban->channel->bans = ban->next;
1258 ban->next->prev = ban->prev;
1261 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1270 expire_ban(void *data)
1272 struct banData *bd = data;
1273 if(!IsSuspended(bd->channel))
1275 struct banList bans;
1276 struct mod_chanmode change;
1278 bans = bd->channel->channel->banlist;
1279 mod_chanmode_init(&change);
1280 for(ii=0; ii<bans.used; ii++)
1282 if(!strcmp(bans.list[ii]->ban, bd->mask))
1285 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1286 change.args[0].u.hostmask = bd->mask;
1287 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1293 del_channel_ban(bd);
1296 static void chanserv_expire_suspension(void *data);
1299 unregister_channel(struct chanData *channel, const char *reason)
1301 struct mod_chanmode change;
1302 char msgbuf[MAXLEN];
1304 /* After channel unregistration, the following must be cleaned
1306 - Channel information.
1309 - Channel suspension data.
1310 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1316 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1320 mod_chanmode_init(&change);
1321 change.modes_clear |= MODE_REGISTERED;
1322 mod_chanmode_announce(chanserv, channel->channel, &change);
1325 while(channel->users)
1326 del_channel_user(channel->users, 0);
1328 while(channel->bans)
1329 del_channel_ban(channel->bans);
1331 free(channel->topic);
1332 free(channel->registrar);
1333 free(channel->greeting);
1334 free(channel->user_greeting);
1335 free(channel->topic_mask);
1338 channel->prev->next = channel->next;
1340 channelList = channel->next;
1343 channel->next->prev = channel->prev;
1345 if(channel->suspended)
1347 struct chanNode *cNode = channel->channel;
1348 struct suspended *suspended, *next_suspended;
1350 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1352 next_suspended = suspended->previous;
1353 free(suspended->suspender);
1354 free(suspended->reason);
1355 if(suspended->expires)
1356 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1361 cNode->channel_info = NULL;
1363 channel->channel->channel_info = NULL;
1365 dict_delete(channel->notes);
1366 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1367 if(!IsSuspended(channel))
1368 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1369 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1370 UnlockChannel(channel->channel);
1372 registered_channels--;
1376 expire_channels(UNUSED_ARG(void *data))
1378 struct chanData *channel, *next;
1379 struct userData *user;
1380 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1382 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1383 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1385 for(channel = channelList; channel; channel = next)
1387 next = channel->next;
1389 /* See if the channel can be expired. */
1390 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1391 || IsProtected(channel))
1394 /* Make sure there are no high-ranking users still in the channel. */
1395 for(user=channel->users; user; user=user->next)
1396 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1401 /* Unregister the channel */
1402 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1403 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1404 unregister_channel(channel, "registration expired.");
1407 if(chanserv_conf.channel_expire_frequency)
1408 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1412 expire_dnrs(UNUSED_ARG(void *data))
1414 dict_iterator_t it, next;
1415 struct do_not_register *dnr;
1417 for(it = dict_first(handle_dnrs); it; it = next)
1419 dnr = iter_data(it);
1420 next = iter_next(it);
1421 if(dnr->expires && dnr->expires <= now)
1422 dict_remove(handle_dnrs, dnr->chan_name + 1);
1424 for(it = dict_first(plain_dnrs); it; it = next)
1426 dnr = iter_data(it);
1427 next = iter_next(it);
1428 if(dnr->expires && dnr->expires <= now)
1429 dict_remove(plain_dnrs, dnr->chan_name + 1);
1431 for(it = dict_first(mask_dnrs); it; it = next)
1433 dnr = iter_data(it);
1434 next = iter_next(it);
1435 if(dnr->expires && dnr->expires <= now)
1436 dict_remove(mask_dnrs, dnr->chan_name + 1);
1439 if(chanserv_conf.dnr_expire_frequency)
1440 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1444 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1446 char protect = channel->chOpts[chProtect];
1447 struct userData *cs_victim, *cs_aggressor;
1449 /* Don't protect if no one is to be protected, someone is attacking
1450 himself, or if the aggressor is an IRC Operator. */
1451 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1454 /* Don't protect if the victim isn't authenticated (because they
1455 can't be a channel user), unless we are to protect non-users
1457 cs_victim = GetChannelAccess(channel, victim->handle_info);
1458 if(protect != 'a' && !cs_victim)
1461 /* Protect if the aggressor isn't a user because at this point,
1462 the aggressor can only be less than or equal to the victim. */
1463 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1467 /* If the aggressor was a user, then the victim can't be helped. */
1474 if(cs_victim->access > cs_aggressor->access)
1479 if(cs_victim->access >= cs_aggressor->access)
1488 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1490 struct chanData *cData = channel->channel_info;
1491 struct userData *cs_victim;
1493 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1494 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1495 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1497 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1505 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1507 if(IsService(victim))
1509 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1513 if(protect_user(victim, user, channel->channel_info))
1515 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1522 static struct do_not_register *
1523 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1525 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1526 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1527 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1528 strcpy(dnr->reason, reason);
1530 dnr->expires = expires;
1531 if(dnr->chan_name[0] == '*')
1532 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1533 else if(strpbrk(dnr->chan_name, "*?"))
1534 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1536 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1540 static struct dnrList
1541 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1543 struct dnrList list;
1544 dict_iterator_t it, next;
1545 struct do_not_register *dnr;
1547 dnrList_init(&list);
1549 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(handle_dnrs, handle);
1553 else if(list.used < max)
1554 dnrList_append(&list, dnr);
1557 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1559 if(dnr->expires && dnr->expires <= now)
1560 dict_remove(plain_dnrs, chan_name);
1561 else if(list.used < max)
1562 dnrList_append(&list, dnr);
1567 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1569 next = iter_next(it);
1570 if(!match_ircglob(chan_name, iter_key(it)))
1572 dnr = iter_data(it);
1573 if(dnr->expires && dnr->expires <= now)
1574 dict_remove(mask_dnrs, iter_key(it));
1576 dnrList_append(&list, dnr);
1583 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1585 struct userNode *user;
1586 char buf1[INTERVALLEN];
1587 char buf2[INTERVALLEN];
1594 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1599 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1600 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1604 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1607 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1612 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1614 struct dnrList list;
1617 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1618 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1619 dnr_print_func(list.list[ii], user);
1621 reply("CSMSG_MORE_DNRS", list.used - ii);
1626 struct do_not_register *
1627 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1629 struct dnrList list;
1630 struct do_not_register *dnr;
1632 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1633 dnr = list.used ? list.list[0] : NULL;
1638 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1640 struct do_not_register *dnr;
1641 dict_iterator_t it, next;
1642 unsigned int matches = 0;
1644 for(it = dict_first(dict); it; it = next)
1646 dnr = iter_data(it);
1647 next = iter_next(it);
1648 if(dnr->expires && dnr->expires <= now)
1650 dict_remove(dict, iter_key(it));
1653 dnr_print_func(dnr, user);
1660 static CHANSERV_FUNC(cmd_noregister)
1664 unsigned long expiry, duration;
1665 unsigned int matches;
1669 reply("CSMSG_DNR_SEARCH_RESULTS");
1670 matches = send_dnrs(user, handle_dnrs);
1671 matches += send_dnrs(user, plain_dnrs);
1672 matches += send_dnrs(user, mask_dnrs);
1674 reply("MSG_MATCH_COUNT", matches);
1676 reply("MSG_NO_MATCHES");
1682 if(!IsChannelName(target) && (*target != '*'))
1684 reply("CSMSG_NOT_DNR", target);
1692 reply("MSG_INVALID_DURATION", argv[2]);
1696 if(!strcmp(argv[2], "0"))
1698 else if((duration = ParseInterval(argv[2])))
1699 expiry = now + duration;
1702 reply("MSG_INVALID_DURATION", argv[2]);
1706 reason = unsplit_string(argv + 3, argc - 3, NULL);
1707 if((*target == '*') && !get_handle_info(target + 1))
1709 reply("MSG_HANDLE_UNKNOWN", target + 1);
1712 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1713 reply("CSMSG_NOREGISTER_CHANNEL", target);
1717 reply("CSMSG_DNR_SEARCH_RESULTS");
1719 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1721 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1723 reply("MSG_NO_MATCHES");
1727 static CHANSERV_FUNC(cmd_allowregister)
1729 const char *chan_name = argv[1];
1731 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1732 || dict_remove(plain_dnrs, chan_name)
1733 || dict_remove(mask_dnrs, chan_name))
1735 reply("CSMSG_DNR_REMOVED", chan_name);
1738 reply("CSMSG_NO_SUCH_DNR", chan_name);
1743 struct userNode *source;
1747 unsigned long min_set, max_set;
1748 unsigned long min_expires, max_expires;
1753 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1755 return !((dnr->set < search->min_set)
1756 || (dnr->set > search->max_set)
1757 || (dnr->expires < search->min_expires)
1758 || (search->max_expires
1759 && ((dnr->expires == 0)
1760 || (dnr->expires > search->max_expires)))
1761 || (search->chan_mask
1762 && !match_ircglob(dnr->chan_name, search->chan_mask))
1763 || (search->setter_mask
1764 && !match_ircglob(dnr->setter, search->setter_mask))
1765 || (search->reason_mask
1766 && !match_ircglob(dnr->reason, search->reason_mask)));
1769 static struct dnr_search *
1770 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1772 struct dnr_search *discrim;
1775 discrim = calloc(1, sizeof(*discrim));
1776 discrim->source = user;
1777 discrim->chan_mask = NULL;
1778 discrim->setter_mask = NULL;
1779 discrim->reason_mask = NULL;
1780 discrim->max_set = INT_MAX;
1781 discrim->limit = 50;
1783 for(ii=0; ii<argc; ++ii)
1787 reply("MSG_MISSING_PARAMS", argv[ii]);
1790 else if(0 == irccasecmp(argv[ii], "channel"))
1792 discrim->chan_mask = argv[++ii];
1794 else if(0 == irccasecmp(argv[ii], "setter"))
1796 discrim->setter_mask = argv[++ii];
1798 else if(0 == irccasecmp(argv[ii], "reason"))
1800 discrim->reason_mask = argv[++ii];
1802 else if(0 == irccasecmp(argv[ii], "limit"))
1804 discrim->limit = strtoul(argv[++ii], NULL, 0);
1806 else if(0 == irccasecmp(argv[ii], "set"))
1808 const char *cmp = argv[++ii];
1811 discrim->min_set = now - ParseInterval(cmp + 2);
1813 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1814 } else if(cmp[0] == '=') {
1815 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1816 } else if(cmp[0] == '>') {
1818 discrim->max_set = now - ParseInterval(cmp + 2);
1820 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1822 discrim->max_set = now - (ParseInterval(cmp) - 1);
1825 else if(0 == irccasecmp(argv[ii], "expires"))
1827 const char *cmp = argv[++ii];
1830 discrim->max_expires = now + ParseInterval(cmp + 2);
1832 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1833 } else if(cmp[0] == '=') {
1834 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1835 } else if(cmp[0] == '>') {
1837 discrim->min_expires = now + ParseInterval(cmp + 2);
1839 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1841 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1846 reply("MSG_INVALID_CRITERIA", argv[ii]);
1857 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1860 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1862 struct do_not_register *dnr;
1863 dict_iterator_t next;
1868 /* Initialize local variables. */
1871 if(discrim->chan_mask)
1873 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1874 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1878 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1880 /* Check against account-based DNRs. */
1881 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1882 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1885 else if(target_fixed)
1887 /* Check against channel-based DNRs. */
1888 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1889 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1894 /* Exhaustively search account DNRs. */
1895 for(it = dict_first(handle_dnrs); it; it = next)
1897 next = iter_next(it);
1898 dnr = iter_data(it);
1899 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1903 /* Do the same for channel DNRs. */
1904 for(it = dict_first(plain_dnrs); it; it = next)
1906 next = iter_next(it);
1907 dnr = iter_data(it);
1908 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1912 /* Do the same for wildcarded channel DNRs. */
1913 for(it = dict_first(mask_dnrs); it; it = next)
1915 next = iter_next(it);
1916 dnr = iter_data(it);
1917 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1925 dnr_remove_func(struct do_not_register *match, void *extra)
1927 struct userNode *user;
1930 chan_name = alloca(strlen(match->chan_name) + 1);
1931 strcpy(chan_name, match->chan_name);
1933 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1934 || dict_remove(plain_dnrs, chan_name)
1935 || dict_remove(mask_dnrs, chan_name))
1937 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1943 dnr_count_func(struct do_not_register *match, void *extra)
1945 return 0; (void)match; (void)extra;
1948 static MODCMD_FUNC(cmd_dnrsearch)
1950 struct dnr_search *discrim;
1951 dnr_search_func action;
1952 struct svccmd *subcmd;
1953 unsigned int matches;
1956 sprintf(buf, "dnrsearch %s", argv[1]);
1957 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1960 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1963 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1965 if(!irccasecmp(argv[1], "print"))
1966 action = dnr_print_func;
1967 else if(!irccasecmp(argv[1], "remove"))
1968 action = dnr_remove_func;
1969 else if(!irccasecmp(argv[1], "count"))
1970 action = dnr_count_func;
1973 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1977 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1981 if(action == dnr_print_func)
1982 reply("CSMSG_DNR_SEARCH_RESULTS");
1983 matches = dnr_search(discrim, action, user);
1985 reply("MSG_MATCH_COUNT", matches);
1987 reply("MSG_NO_MATCHES");
1993 chanserv_get_owned_count(struct handle_info *hi)
1995 struct userData *cList;
1998 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1999 if(cList->access == UL_OWNER)
2004 static CHANSERV_FUNC(cmd_register)
2006 struct handle_info *handle;
2007 struct chanData *cData;
2008 struct modeNode *mn;
2009 char reason[MAXLEN];
2011 unsigned int new_channel, force=0;
2012 struct do_not_register *dnr;
2016 if(channel->channel_info)
2018 reply("CSMSG_ALREADY_REGGED", channel->name);
2022 if(channel->bad_channel)
2024 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2029 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2031 reply("CSMSG_MUST_BE_OPPED", channel->name);
2036 chan_name = channel->name;
2040 if((argc < 2) || !IsChannelName(argv[1]))
2042 reply("MSG_NOT_CHANNEL_NAME");
2046 if(opserv_bad_channel(argv[1]))
2048 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2053 chan_name = argv[1];
2056 if(argc >= (new_channel+2))
2058 if(!IsHelping(user))
2060 reply("CSMSG_PROXY_FORBIDDEN");
2064 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2066 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2067 dnr = chanserv_is_dnr(chan_name, handle);
2071 handle = user->handle_info;
2072 dnr = chanserv_is_dnr(chan_name, handle);
2076 if(!IsHelping(user))
2077 reply("CSMSG_DNR_CHANNEL", chan_name);
2079 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2083 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2085 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2090 channel = AddChannel(argv[1], now, NULL, NULL);
2092 cData = register_channel(channel, user->handle_info->handle);
2093 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2094 cData->modes = chanserv_conf.default_modes;
2096 cData->modes.modes_set |= MODE_REGISTERED;
2097 if (IsOffChannel(cData))
2099 mod_chanmode_announce(chanserv, channel, &cData->modes);
2103 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2104 change->args[change->argc].mode = MODE_CHANOP;
2105 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2107 mod_chanmode_announce(chanserv, channel, change);
2108 mod_chanmode_free(change);
2111 /* Initialize the channel's max user record. */
2112 cData->max = channel->members.used;
2114 if(handle != user->handle_info)
2115 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2117 reply("CSMSG_REG_SUCCESS", channel->name);
2119 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2120 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2125 make_confirmation_string(struct userData *uData)
2127 static char strbuf[16];
2132 for(src = uData->handle->handle; *src; )
2133 accum = accum * 31 + toupper(*src++);
2135 for(src = uData->channel->channel->name; *src; )
2136 accum = accum * 31 + toupper(*src++);
2137 sprintf(strbuf, "%08x", accum);
2141 static CHANSERV_FUNC(cmd_unregister)
2144 char reason[MAXLEN];
2145 struct chanData *cData;
2146 struct userData *uData;
2148 cData = channel->channel_info;
2151 reply("CSMSG_NOT_REGISTERED", channel->name);
2155 uData = GetChannelUser(cData, user->handle_info);
2156 if(!uData || (uData->access < UL_OWNER))
2158 reply("CSMSG_NO_ACCESS");
2162 if(IsProtected(cData))
2164 reply("CSMSG_UNREG_NODELETE", channel->name);
2168 if(!IsHelping(user))
2170 const char *confirm_string;
2171 if(IsSuspended(cData))
2173 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2176 confirm_string = make_confirmation_string(uData);
2177 if((argc < 2) || strcmp(argv[1], confirm_string))
2179 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2184 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2185 name = strdup(channel->name);
2186 unregister_channel(cData, reason);
2187 spamserv_cs_unregister(user, channel, manually, "unregistered");
2188 reply("CSMSG_UNREG_SUCCESS", name);
2194 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2196 extern struct userNode *spamserv;
2197 struct mod_chanmode *change;
2199 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2201 change = mod_chanmode_alloc(2);
2203 change->args[0].mode = MODE_CHANOP;
2204 change->args[0].u.member = AddChannelUser(chanserv, channel);
2205 change->args[1].mode = MODE_CHANOP;
2206 change->args[1].u.member = AddChannelUser(spamserv, channel);
2210 change = mod_chanmode_alloc(1);
2212 change->args[0].mode = MODE_CHANOP;
2213 change->args[0].u.member = AddChannelUser(chanserv, channel);
2216 mod_chanmode_announce(chanserv, channel, change);
2217 mod_chanmode_free(change);
2220 static CHANSERV_FUNC(cmd_move)
2222 struct mod_chanmode change;
2223 struct chanNode *target;
2224 struct modeNode *mn;
2225 struct userData *uData;
2226 char reason[MAXLEN];
2227 struct do_not_register *dnr;
2228 int chanserv_join = 0, spamserv_join;
2232 if(IsProtected(channel->channel_info))
2234 reply("CSMSG_MOVE_NODELETE", channel->name);
2238 if(!IsChannelName(argv[1]))
2240 reply("MSG_NOT_CHANNEL_NAME");
2244 if(opserv_bad_channel(argv[1]))
2246 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2250 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2252 for(uData = channel->channel_info->users; uData; uData = uData->next)
2254 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2256 if(!IsHelping(user))
2257 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2259 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2265 mod_chanmode_init(&change);
2266 if(!(target = GetChannel(argv[1])))
2268 target = AddChannel(argv[1], now, NULL, NULL);
2269 if(!IsSuspended(channel->channel_info))
2272 else if(target->channel_info)
2274 reply("CSMSG_ALREADY_REGGED", target->name);
2277 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2278 && !IsHelping(user))
2280 reply("CSMSG_MUST_BE_OPPED", target->name);
2283 else if(!IsSuspended(channel->channel_info))
2288 /* Clear MODE_REGISTERED from old channel, add it to new. */
2290 change.modes_clear = MODE_REGISTERED;
2291 mod_chanmode_announce(chanserv, channel, &change);
2292 change.modes_clear = 0;
2293 change.modes_set = MODE_REGISTERED;
2294 mod_chanmode_announce(chanserv, target, &change);
2297 /* Move the channel_info to the target channel; it
2298 shouldn't be necessary to clear timeq callbacks
2299 for the old channel. */
2300 target->channel_info = channel->channel_info;
2301 target->channel_info->channel = target;
2302 channel->channel_info = NULL;
2304 /* Check whether users are present in the new channel. */
2305 for(uData = target->channel_info->users; uData; uData = uData->next)
2306 scan_user_presence(uData, NULL);
2308 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2311 ss_cs_join_channel(target, spamserv_join);
2313 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2314 if(!IsSuspended(target->channel_info))
2316 char reason2[MAXLEN];
2317 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2318 DelChannelUser(chanserv, channel, reason2, 0);
2320 UnlockChannel(channel);
2321 LockChannel(target);
2322 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2323 reply("CSMSG_MOVE_SUCCESS", target->name);
2328 merge_users(struct chanData *source, struct chanData *target)
2330 struct userData *suData, *tuData, *next;
2336 /* Insert the source's users into the scratch area. */
2337 for(suData = source->users; suData; suData = suData->next)
2338 dict_insert(merge, suData->handle->handle, suData);
2340 /* Iterate through the target's users, looking for
2341 users common to both channels. The lower access is
2342 removed from either the scratch area or target user
2344 for(tuData = target->users; tuData; tuData = next)
2346 struct userData *choice;
2348 next = tuData->next;
2350 /* If a source user exists with the same handle as a target
2351 channel's user, resolve the conflict by removing one. */
2352 suData = dict_find(merge, tuData->handle->handle, NULL);
2356 /* Pick the data we want to keep. */
2357 /* If the access is the same, use the later seen time. */
2358 if(suData->access == tuData->access)
2359 choice = (suData->seen > tuData->seen) ? suData : tuData;
2360 else /* Otherwise, keep the higher access level. */
2361 choice = (suData->access > tuData->access) ? suData : tuData;
2362 /* Use the later seen time. */
2363 if(suData->seen < tuData->seen)
2364 suData->seen = tuData->seen;
2366 tuData->seen = suData->seen;
2368 /* Remove the user that wasn't picked. */
2369 if(choice == tuData)
2371 dict_remove(merge, suData->handle->handle);
2372 del_channel_user(suData, 0);
2375 del_channel_user(tuData, 0);
2378 /* Move the remaining users to the target channel. */
2379 for(it = dict_first(merge); it; it = iter_next(it))
2381 suData = iter_data(it);
2383 /* Insert the user into the target channel's linked list. */
2384 suData->prev = NULL;
2385 suData->next = target->users;
2386 suData->channel = target;
2389 target->users->prev = suData;
2390 target->users = suData;
2392 /* Update the user counts for the target channel; the
2393 source counts are left alone. */
2394 target->userCount++;
2396 /* Check whether the user is in the target channel. */
2397 scan_user_presence(suData, NULL);
2400 /* Possible to assert (source->users == NULL) here. */
2401 source->users = NULL;
2406 merge_bans(struct chanData *source, struct chanData *target)
2408 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2410 /* Hold on to the original head of the target ban list
2411 to avoid comparing source bans with source bans. */
2412 tFront = target->bans;
2414 /* Perform a totally expensive O(n*m) merge, ick. */
2415 for(sbData = source->bans; sbData; sbData = sNext)
2417 /* Flag to track whether the ban's been moved
2418 to the destination yet. */
2421 /* Possible to assert (sbData->prev == NULL) here. */
2422 sNext = sbData->next;
2424 for(tbData = tFront; tbData; tbData = tNext)
2426 tNext = tbData->next;
2428 /* Perform two comparisons between each source
2429 and target ban, conflicts are resolved by
2430 keeping the broader ban and copying the later
2431 expiration and triggered time. */
2432 if(match_ircglobs(tbData->mask, sbData->mask))
2434 /* There is a broader ban in the target channel that
2435 overrides one in the source channel; remove the
2436 source ban and break. */
2437 if(sbData->expires > tbData->expires)
2438 tbData->expires = sbData->expires;
2439 if(sbData->triggered > tbData->triggered)
2440 tbData->triggered = sbData->triggered;
2441 del_channel_ban(sbData);
2444 else if(match_ircglobs(sbData->mask, tbData->mask))
2446 /* There is a broader ban in the source channel that
2447 overrides one in the target channel; remove the
2448 target ban, fall through and move the source over. */
2449 if(tbData->expires > sbData->expires)
2450 sbData->expires = tbData->expires;
2451 if(tbData->triggered > sbData->triggered)
2452 sbData->triggered = tbData->triggered;
2453 if(tbData == tFront)
2455 del_channel_ban(tbData);
2458 /* Source bans can override multiple target bans, so
2459 we allow a source to run through this loop multiple
2460 times, but we can only move it once. */
2465 /* Remove the source ban from the source ban list. */
2467 sbData->next->prev = sbData->prev;
2469 /* Modify the source ban's associated channel. */
2470 sbData->channel = target;
2472 /* Insert the ban into the target channel's linked list. */
2473 sbData->prev = NULL;
2474 sbData->next = target->bans;
2477 target->bans->prev = sbData;
2478 target->bans = sbData;
2480 /* Update the user counts for the target channel. */
2485 /* Possible to assert (source->bans == NULL) here. */
2486 source->bans = NULL;
2490 merge_data(struct chanData *source, struct chanData *target)
2492 /* Use more recent visited and owner-transfer time; use older
2493 * registered time. Bitwise or may_opchan. Use higher max.
2494 * Do not touch last_refresh, ban count or user counts.
2496 if(source->visited > target->visited)
2497 target->visited = source->visited;
2498 if(source->registered < target->registered)
2499 target->registered = source->registered;
2500 if(source->ownerTransfer > target->ownerTransfer)
2501 target->ownerTransfer = source->ownerTransfer;
2502 if(source->may_opchan)
2503 target->may_opchan = 1;
2504 if(source->max > target->max)
2505 target->max = source->max;
2509 merge_channel(struct chanData *source, struct chanData *target)
2511 merge_users(source, target);
2512 merge_bans(source, target);
2513 merge_data(source, target);
2516 static CHANSERV_FUNC(cmd_merge)
2518 struct userData *target_user;
2519 struct chanNode *target;
2520 char reason[MAXLEN];
2524 /* Make sure the target channel exists and is registered to the user
2525 performing the command. */
2526 if(!(target = GetChannel(argv[1])))
2528 reply("MSG_INVALID_CHANNEL");
2532 if(!target->channel_info)
2534 reply("CSMSG_NOT_REGISTERED", target->name);
2538 if(IsProtected(channel->channel_info))
2540 reply("CSMSG_MERGE_NODELETE");
2544 if(IsSuspended(target->channel_info))
2546 reply("CSMSG_MERGE_SUSPENDED");
2550 if(channel == target)
2552 reply("CSMSG_MERGE_SELF");
2556 target_user = GetChannelUser(target->channel_info, user->handle_info);
2557 if(!target_user || (target_user->access < UL_OWNER))
2559 reply("CSMSG_MERGE_NOT_OWNER");
2563 /* Merge the channel structures and associated data. */
2564 merge_channel(channel->channel_info, target->channel_info);
2565 spamserv_cs_move_merge(user, channel, target, 0);
2566 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2567 unregister_channel(channel->channel_info, reason);
2568 reply("CSMSG_MERGE_SUCCESS", target->name);
2572 static CHANSERV_FUNC(cmd_opchan)
2574 struct mod_chanmode change;
2575 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2577 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2580 channel->channel_info->may_opchan = 0;
2581 mod_chanmode_init(&change);
2583 change.args[0].mode = MODE_CHANOP;
2584 change.args[0].u.member = GetUserMode(channel, chanserv);
2585 if(!change.args[0].u.member)
2587 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2590 mod_chanmode_announce(chanserv, channel, &change);
2591 reply("CSMSG_OPCHAN_DONE", channel->name);
2595 static CHANSERV_FUNC(cmd_adduser)
2597 struct userData *actee;
2598 struct userData *actor, *real_actor;
2599 struct handle_info *handle;
2600 unsigned short access_level, override = 0;
2604 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2606 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2610 access_level = user_level_from_name(argv[2], UL_OWNER);
2613 reply("CSMSG_INVALID_ACCESS", argv[2]);
2617 actor = GetChannelUser(channel->channel_info, user->handle_info);
2618 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2620 if(actor->access <= access_level)
2622 reply("CSMSG_NO_BUMP_ACCESS");
2626 /* Trying to add someone with equal/more access? */
2627 if (!real_actor || real_actor->access <= access_level)
2628 override = CMD_LOG_OVERRIDE;
2630 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2633 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2635 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2639 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2640 scan_user_presence(actee, NULL);
2641 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2642 return 1 | override;
2645 static CHANSERV_FUNC(cmd_clvl)
2647 struct handle_info *handle;
2648 struct userData *victim;
2649 struct userData *actor, *real_actor;
2650 unsigned short new_access, override = 0;
2651 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2655 actor = GetChannelUser(channel->channel_info, user->handle_info);
2656 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2658 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2661 if(handle == user->handle_info && !privileged)
2663 reply("CSMSG_NO_SELF_CLVL");
2667 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2669 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2673 if(actor->access <= victim->access && !privileged)
2675 reply("MSG_USER_OUTRANKED", handle->handle);
2679 new_access = user_level_from_name(argv[2], UL_OWNER);
2683 reply("CSMSG_INVALID_ACCESS", argv[2]);
2687 if(new_access >= actor->access && !privileged)
2689 reply("CSMSG_NO_BUMP_ACCESS");
2693 /* Trying to clvl a equal/higher user? */
2694 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2695 override = CMD_LOG_OVERRIDE;
2696 /* Trying to clvl someone to equal/higher access? */
2697 if(!real_actor || new_access >= real_actor->access)
2698 override = CMD_LOG_OVERRIDE;
2699 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2700 * If they lower their own access it's not a big problem.
2703 victim->access = new_access;
2704 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2705 return 1 | override;
2708 static CHANSERV_FUNC(cmd_deluser)
2710 struct handle_info *handle;
2711 struct userData *victim;
2712 struct userData *actor, *real_actor;
2713 unsigned short access_level, override = 0;
2718 actor = GetChannelUser(channel->channel_info, user->handle_info);
2719 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2721 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2724 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2726 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2732 access_level = user_level_from_name(argv[1], UL_OWNER);
2735 reply("CSMSG_INVALID_ACCESS", argv[1]);
2738 if(access_level != victim->access)
2740 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2746 access_level = victim->access;
2749 if((actor->access <= victim->access) && !IsHelping(user))
2751 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2755 /* If people delete themselves it is an override, but they
2756 * could've used deleteme so we don't log it as an override
2758 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2759 override = CMD_LOG_OVERRIDE;
2761 chan_name = strdup(channel->name);
2762 del_channel_user(victim, 1);
2763 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2765 return 1 | override;
2769 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2771 struct userData *actor, *real_actor, *uData, *next;
2772 unsigned int override = 0;
2774 actor = GetChannelUser(channel->channel_info, user->handle_info);
2775 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2777 if(min_access > max_access)
2779 reply("CSMSG_BAD_RANGE", min_access, max_access);
2783 if(actor->access <= max_access)
2785 reply("CSMSG_NO_ACCESS");
2789 if(!real_actor || real_actor->access <= max_access)
2790 override = CMD_LOG_OVERRIDE;
2792 for(uData = channel->channel_info->users; uData; uData = next)
2796 if((uData->access >= min_access)
2797 && (uData->access <= max_access)
2798 && match_ircglob(uData->handle->handle, mask))
2799 del_channel_user(uData, 1);
2802 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2803 return 1 | override;
2806 static CHANSERV_FUNC(cmd_mdelowner)
2808 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2811 static CHANSERV_FUNC(cmd_mdelcoowner)
2813 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2816 static CHANSERV_FUNC(cmd_mdelmaster)
2818 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2821 static CHANSERV_FUNC(cmd_mdelop)
2823 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2826 static CHANSERV_FUNC(cmd_mdelpeon)
2828 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2832 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2834 struct banData *bData, *next;
2835 char interval[INTERVALLEN];
2837 unsigned long limit;
2840 limit = now - duration;
2841 for(bData = channel->channel_info->bans; bData; bData = next)
2845 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2848 del_channel_ban(bData);
2852 intervalString(interval, duration, user->handle_info);
2853 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2858 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2860 struct userData *actor, *uData, *next;
2861 char interval[INTERVALLEN];
2863 unsigned long limit;
2865 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2866 if(min_access > max_access)
2868 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2872 if(!actor || actor->access <= max_access)
2874 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2879 limit = now - duration;
2880 for(uData = channel->channel_info->users; uData; uData = next)
2884 if((uData->seen > limit)
2886 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2889 if(((uData->access >= min_access) && (uData->access <= max_access))
2890 || (!max_access && (uData->access < actor->access)))
2892 del_channel_user(uData, 1);
2900 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2902 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2906 static CHANSERV_FUNC(cmd_trim)
2908 unsigned long duration;
2909 unsigned short min_level, max_level;
2914 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2915 duration = ParseInterval(argv[2]);
2918 reply("CSMSG_CANNOT_TRIM");
2922 if(!irccasecmp(argv[1], "bans"))
2924 cmd_trim_bans(user, channel, duration);
2927 else if(!irccasecmp(argv[1], "users"))
2929 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2932 else if(parse_level_range(&min_level, &max_level, argv[1]))
2934 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2937 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2939 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2944 reply("CSMSG_INVALID_TRIM", argv[1]);
2949 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2950 to the user. cmd_all takes advantage of this. */
2951 static CHANSERV_FUNC(cmd_up)
2953 struct mod_chanmode change;
2954 struct userData *uData;
2957 mod_chanmode_init(&change);
2959 change.args[0].u.member = GetUserMode(channel, user);
2960 if(!change.args[0].u.member)
2963 reply("MSG_CHANNEL_ABSENT", channel->name);
2967 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2971 reply("CSMSG_GODMODE_UP", argv[0]);
2974 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2976 change.args[0].mode = MODE_CHANOP;
2977 errmsg = "CSMSG_ALREADY_OPPED";
2979 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2981 change.args[0].mode = MODE_VOICE;
2982 errmsg = "CSMSG_ALREADY_VOICED";
2987 reply("CSMSG_NO_ACCESS");
2990 change.args[0].mode &= ~change.args[0].u.member->modes;
2991 if(!change.args[0].mode)
2994 reply(errmsg, channel->name);
2997 modcmd_chanmode_announce(&change);
3001 static CHANSERV_FUNC(cmd_down)
3003 struct mod_chanmode change;
3005 mod_chanmode_init(&change);
3007 change.args[0].u.member = GetUserMode(channel, user);
3008 if(!change.args[0].u.member)
3011 reply("MSG_CHANNEL_ABSENT", channel->name);
3015 if(!change.args[0].u.member->modes)
3018 reply("CSMSG_ALREADY_DOWN", channel->name);
3022 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3023 modcmd_chanmode_announce(&change);
3027 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)
3029 struct userData *cList;
3031 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3033 if(IsSuspended(cList->channel)
3034 || IsUserSuspended(cList)
3035 || !GetUserMode(cList->channel->channel, user))
3038 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3044 static CHANSERV_FUNC(cmd_upall)
3046 return cmd_all(CSFUNC_ARGS, cmd_up);
3049 static CHANSERV_FUNC(cmd_downall)
3051 return cmd_all(CSFUNC_ARGS, cmd_down);
3054 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3055 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3058 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)
3060 unsigned int ii, valid;
3061 struct userNode *victim;
3062 struct mod_chanmode *change;
3064 change = mod_chanmode_alloc(argc - 1);
3066 for(ii=valid=0; ++ii < argc; )
3068 if(!(victim = GetUserH(argv[ii])))
3070 change->args[valid].mode = mode;
3071 change->args[valid].u.member = GetUserMode(channel, victim);
3072 if(!change->args[valid].u.member)
3074 if(validate && !validate(user, channel, victim))
3079 change->argc = valid;
3080 if(valid < (argc-1))
3081 reply("CSMSG_PROCESS_FAILED");
3084 modcmd_chanmode_announce(change);
3085 reply(action, channel->name);
3087 mod_chanmode_free(change);
3091 static CHANSERV_FUNC(cmd_op)
3093 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3096 static CHANSERV_FUNC(cmd_deop)
3098 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3101 static CHANSERV_FUNC(cmd_voice)
3103 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3106 static CHANSERV_FUNC(cmd_devoice)
3108 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3112 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3118 for(ii=0; ii<channel->members.used; ii++)
3120 struct modeNode *mn = channel->members.list[ii];
3122 if(IsService(mn->user))
3125 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3128 if(protect_user(mn->user, user, channel->channel_info))
3132 victims[(*victimCount)++] = mn;
3138 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3140 struct userNode *victim;
3141 struct modeNode **victims;
3142 unsigned int offset, n, victimCount, duration = 0;
3143 char *reason = "Bye.", *ban, *name;
3144 char interval[INTERVALLEN];
3146 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3147 REQUIRE_PARAMS(offset);
3150 reason = unsplit_string(argv + offset, argc - offset, NULL);
3151 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3153 /* Truncate the reason to a length of TOPICLEN, as
3154 the ircd does; however, leave room for an ellipsis
3155 and the kicker's nick. */
3156 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3160 if((victim = GetUserH(argv[1])))
3162 victims = alloca(sizeof(victims[0]));
3163 victims[0] = GetUserMode(channel, victim);
3164 /* XXX: The comparison with ACTION_KICK is just because all
3165 * other actions can work on users outside the channel, and we
3166 * want to allow those (e.g. unbans) in that case. If we add
3167 * some other ejection action for in-channel users, change
3169 victimCount = victims[0] ? 1 : 0;
3171 if(IsService(victim))
3173 reply("MSG_SERVICE_IMMUNE", victim->nick);
3177 if((action == ACTION_KICK) && !victimCount)
3179 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3183 if(protect_user(victim, user, channel->channel_info))
3185 reply("CSMSG_USER_PROTECTED", victim->nick);
3189 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3190 name = victim->nick;
3192 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3194 struct handle_info *hi;
3195 extern const char *titlehost_suffix;
3196 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3197 const char *accountname = argv[1] + 1;
3199 if(!(hi = get_handle_info(accountname)))
3201 reply("MSG_HANDLE_UNKNOWN", accountname);
3205 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3206 victims = alloca(sizeof(victims[0]) * channel->members.used);
3208 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3210 reply("CSMSG_MASK_PROTECTED", banmask);
3214 if((action == ACTION_KICK) && (victimCount == 0))
3216 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3220 name = ban = strdup(banmask);
3224 if(!is_ircmask(argv[1]))
3226 reply("MSG_NICK_UNKNOWN", argv[1]);
3230 victims = alloca(sizeof(victims[0]) * channel->members.used);
3232 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3234 reply("CSMSG_MASK_PROTECTED", argv[1]);
3238 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3240 reply("CSMSG_LAME_MASK", argv[1]);
3244 if((action == ACTION_KICK) && (victimCount == 0))
3246 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3250 name = ban = strdup(argv[1]);
3253 /* Truncate the ban in place if necessary; we must ensure
3254 that 'ban' is a valid ban mask before sanitizing it. */
3255 sanitize_ircmask(ban);
3257 if(action & ACTION_ADD_BAN)
3259 struct banData *bData, *next;
3261 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3263 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3268 if(action & ACTION_ADD_TIMED_BAN)
3270 duration = ParseInterval(argv[2]);
3274 reply("CSMSG_DURATION_TOO_LOW");
3278 else if(duration > (86400 * 365 * 2))
3280 reply("CSMSG_DURATION_TOO_HIGH");
3286 for(bData = channel->channel_info->bans; bData; bData = next)
3288 if(match_ircglobs(bData->mask, ban))
3290 int exact = !irccasecmp(bData->mask, ban);
3292 /* The ban is redundant; there is already a ban
3293 with the same effect in place. */
3297 free(bData->reason);
3298 bData->reason = strdup(reason);
3299 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3301 reply("CSMSG_REASON_CHANGE", ban);
3305 if(exact && bData->expires)
3309 /* If the ban matches an existing one exactly,
3310 extend the expiration time if the provided
3311 duration is longer. */
3312 if(duration && (now + duration > bData->expires))
3314 bData->expires = now + duration;
3325 /* Delete the expiration timeq entry and
3326 requeue if necessary. */
3327 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3330 timeq_add(bData->expires, expire_ban, bData);
3334 /* automated kickban */
3337 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3339 reply("CSMSG_BAN_ADDED", name, channel->name);
3345 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3352 if(match_ircglobs(ban, bData->mask))
3354 /* The ban we are adding makes previously existing
3355 bans redundant; silently remove them. */
3356 del_channel_ban(bData);
3360 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);
3362 name = ban = strdup(bData->mask);
3366 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3368 extern const char *hidden_host_suffix;
3369 const char *old_name = chanserv_conf.old_ban_names->list[n];
3371 unsigned int l1, l2;
3374 l2 = strlen(old_name);
3377 if(irccasecmp(ban + l1 - l2, old_name))
3379 new_mask = malloc(MAXLEN);
3380 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3382 name = ban = new_mask;
3387 if(action & ACTION_BAN)
3389 unsigned int exists;
3390 struct mod_chanmode *change;
3392 if(channel->banlist.used >= MAXBANS)
3395 reply("CSMSG_BANLIST_FULL", channel->name);
3400 exists = ChannelBanExists(channel, ban);
3401 change = mod_chanmode_alloc(victimCount + 1);
3402 for(n = 0; n < victimCount; ++n)
3404 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3405 change->args[n].u.member = victims[n];
3409 change->args[n].mode = MODE_BAN;
3410 change->args[n++].u.hostmask = ban;
3414 modcmd_chanmode_announce(change);
3416 mod_chanmode_announce(chanserv, channel, change);
3417 mod_chanmode_free(change);
3419 if(exists && (action == ACTION_BAN))
3422 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3428 if(action & ACTION_KICK)
3430 char kick_reason[MAXLEN];
3431 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3433 for(n = 0; n < victimCount; n++)
3434 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3439 /* No response, since it was automated. */
3441 else if(action & ACTION_ADD_BAN)
3444 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3446 reply("CSMSG_BAN_ADDED", name, channel->name);
3448 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3449 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3450 else if(action & ACTION_BAN)
3451 reply("CSMSG_BAN_DONE", name, channel->name);
3452 else if(action & ACTION_KICK && victimCount)
3453 reply("CSMSG_KICK_DONE", name, channel->name);
3459 static CHANSERV_FUNC(cmd_kickban)
3461 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3464 static CHANSERV_FUNC(cmd_kick)
3466 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3469 static CHANSERV_FUNC(cmd_ban)
3471 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3474 static CHANSERV_FUNC(cmd_addban)
3476 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3479 static CHANSERV_FUNC(cmd_addtimedban)
3481 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3484 static struct mod_chanmode *
3485 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3487 struct mod_chanmode *change;
3488 unsigned char *match;
3489 unsigned int ii, count;
3491 match = alloca(bans->used);
3494 for(ii = count = 0; ii < bans->used; ++ii)
3496 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3497 MATCH_USENICK | MATCH_VISIBLE);
3504 for(ii = count = 0; ii < bans->used; ++ii)
3506 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3513 change = mod_chanmode_alloc(count);
3514 for(ii = count = 0; ii < bans->used; ++ii)
3518 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3519 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3521 assert(count == change->argc);
3526 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3528 struct userNode *actee;
3534 /* may want to allow a comma delimited list of users... */
3535 if(!(actee = GetUserH(argv[1])))
3537 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3539 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3540 const char *accountname = argv[1] + 1;
3542 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3543 mask = strdup(banmask);
3545 else if(!is_ircmask(argv[1]))
3547 reply("MSG_NICK_UNKNOWN", argv[1]);
3552 mask = strdup(argv[1]);
3556 /* We don't sanitize the mask here because ircu
3558 if(action & ACTION_UNBAN)
3560 struct mod_chanmode *change;
3561 change = find_matching_bans(&channel->banlist, actee, mask);
3566 modcmd_chanmode_announce(change);
3567 for(ii = 0; ii < change->argc; ++ii)
3568 free((char*)change->args[ii].u.hostmask);
3569 mod_chanmode_free(change);
3574 if(action & ACTION_DEL_BAN)
3576 struct banData *ban, *next;
3578 ban = channel->channel_info->bans;
3582 for( ; ban && !user_matches_glob(actee, ban->mask,
3583 MATCH_USENICK | MATCH_VISIBLE);
3586 for( ; ban && !match_ircglobs(mask, ban->mask);
3591 del_channel_ban(ban);
3598 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3600 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3606 static CHANSERV_FUNC(cmd_unban)
3608 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3611 static CHANSERV_FUNC(cmd_delban)
3613 /* it doesn't necessarily have to remove the channel ban - may want
3614 to make that an option. */
3615 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3618 static CHANSERV_FUNC(cmd_unbanme)
3620 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3621 long flags = ACTION_UNBAN;
3623 /* remove permanent bans if the user has the proper access. */
3624 if(uData->access >= UL_MASTER)
3625 flags |= ACTION_DEL_BAN;
3627 argv[1] = user->nick;
3628 return unban_user(user, channel, 2, argv, cmd, flags);
3631 static CHANSERV_FUNC(cmd_unbanall)
3633 struct mod_chanmode *change;
3636 if(!channel->banlist.used)
3638 reply("CSMSG_NO_BANS", channel->name);
3642 change = mod_chanmode_alloc(channel->banlist.used);
3643 for(ii=0; ii<channel->banlist.used; ii++)
3645 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3646 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3648 modcmd_chanmode_announce(change);
3649 for(ii = 0; ii < change->argc; ++ii)
3650 free((char*)change->args[ii].u.hostmask);
3651 mod_chanmode_free(change);
3652 reply("CSMSG_BANS_REMOVED", channel->name);
3656 static CHANSERV_FUNC(cmd_open)
3658 struct mod_chanmode *change;
3661 change = find_matching_bans(&channel->banlist, user, NULL);
3663 change = mod_chanmode_alloc(0);
3664 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3665 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3666 && channel->channel_info->modes.modes_set)
3667 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3668 modcmd_chanmode_announce(change);
3669 reply("CSMSG_CHANNEL_OPENED", channel->name);
3670 for(ii = 0; ii < change->argc; ++ii)
3671 free((char*)change->args[ii].u.hostmask);
3672 mod_chanmode_free(change);
3676 static CHANSERV_FUNC(cmd_myaccess)
3678 static struct string_buffer sbuf;
3679 struct handle_info *target_handle;
3680 struct userData *uData;
3683 target_handle = user->handle_info;
3684 else if(!IsStaff(user))
3686 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3689 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3692 if(!oper_outranks(user, target_handle))
3695 if(!target_handle->channels)
3697 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3701 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3702 for(uData = target_handle->channels; uData; uData = uData->u_next)
3704 struct chanData *cData = uData->channel;
3706 if(uData->access > UL_OWNER)
3708 if(IsProtected(cData)
3709 && (target_handle != user->handle_info)
3710 && !GetTrueChannelAccess(cData, user->handle_info))
3713 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3714 if(uData->flags != USER_AUTO_OP)
3715 string_buffer_append(&sbuf, ',');
3716 if(IsUserSuspended(uData))
3717 string_buffer_append(&sbuf, 's');
3718 if(IsUserAutoOp(uData))
3720 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3721 string_buffer_append(&sbuf, 'o');
3722 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3723 string_buffer_append(&sbuf, 'v');
3725 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3726 string_buffer_append(&sbuf, 'i');
3728 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3730 string_buffer_append_string(&sbuf, ")]");
3731 string_buffer_append(&sbuf, '\0');
3732 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3738 static CHANSERV_FUNC(cmd_access)
3740 struct userNode *target;
3741 struct handle_info *target_handle;
3742 struct userData *uData;
3744 char prefix[MAXLEN];
3749 target_handle = target->handle_info;
3751 else if((target = GetUserH(argv[1])))
3753 target_handle = target->handle_info;
3755 else if(argv[1][0] == '*')
3757 if(!(target_handle = get_handle_info(argv[1]+1)))
3759 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3765 reply("MSG_NICK_UNKNOWN", argv[1]);
3769 assert(target || target_handle);
3771 if(target == chanserv)
3773 reply("CSMSG_IS_CHANSERV");
3781 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3786 reply("MSG_USER_AUTHENTICATE", target->nick);
3789 reply("MSG_AUTHENTICATE");
3795 const char *epithet = NULL, *type = NULL;
3798 epithet = chanserv_conf.irc_operator_epithet;
3799 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3801 else if(IsNetworkHelper(target))
3803 epithet = chanserv_conf.network_helper_epithet;
3804 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3806 else if(IsSupportHelper(target))
3808 epithet = chanserv_conf.support_helper_epithet;
3809 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3813 if(target_handle->epithet)
3814 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3816 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3818 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3822 sprintf(prefix, "%s", target_handle->handle);
3825 if(!channel->channel_info)
3827 reply("CSMSG_NOT_REGISTERED", channel->name);
3831 helping = HANDLE_FLAGGED(target_handle, HELPING)
3832 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3833 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3835 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3836 /* To prevent possible information leaks, only show infolines
3837 * if the requestor is in the channel or it's their own
3839 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3841 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3843 /* Likewise, only say it's suspended if the user has active
3844 * access in that channel or it's their own entry. */
3845 if(IsUserSuspended(uData)
3846 && (GetChannelUser(channel->channel_info, user->handle_info)
3847 || (user->handle_info == uData->handle)))
3849 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3854 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3861 zoot_list(struct listData *list)
3863 struct userData *uData;
3864 unsigned int start, curr, highest, lowest;
3865 struct helpfile_table tmp_table;
3866 const char **temp, *msg;
3868 if(list->table.length == 1)
3871 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3873 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3874 msg = user_find_message(list->user, "MSG_NONE");
3875 send_message_type(4, list->user, list->bot, " %s", msg);
3877 tmp_table.width = list->table.width;
3878 tmp_table.flags = list->table.flags;
3879 list->table.contents[0][0] = " ";
3880 highest = list->highest;
3881 if(list->lowest != 0)
3882 lowest = list->lowest;
3883 else if(highest < 100)
3886 lowest = highest - 100;
3887 for(start = curr = 1; curr < list->table.length; )
3889 uData = list->users[curr-1];
3890 list->table.contents[curr++][0] = " ";
3891 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3894 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3896 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3897 temp = list->table.contents[--start];
3898 list->table.contents[start] = list->table.contents[0];
3899 tmp_table.contents = list->table.contents + start;
3900 tmp_table.length = curr - start;
3901 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3902 list->table.contents[start] = temp;
3904 highest = lowest - 1;
3905 lowest = (highest < 100) ? 0 : (highest - 99);
3911 def_list(struct listData *list)
3915 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3917 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3918 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3919 if(list->table.length == 1)
3921 msg = user_find_message(list->user, "MSG_NONE");
3922 send_message_type(4, list->user, list->bot, " %s", msg);
3927 userData_access_comp(const void *arg_a, const void *arg_b)
3929 const struct userData *a = *(struct userData**)arg_a;
3930 const struct userData *b = *(struct userData**)arg_b;
3932 if(a->access != b->access)
3933 res = b->access - a->access;
3935 res = irccasecmp(a->handle->handle, b->handle->handle);
3940 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3942 void (*send_list)(struct listData *);
3943 struct userData *uData;
3944 struct listData lData;
3945 unsigned int matches;
3949 lData.bot = cmd->parent->bot;
3950 lData.channel = channel;
3951 lData.lowest = lowest;
3952 lData.highest = highest;
3953 lData.search = (argc > 1) ? argv[1] : NULL;
3954 send_list = def_list;
3955 (void)zoot_list; /* since it doesn't show user levels */
3957 if(user->handle_info)
3959 switch(user->handle_info->userlist_style)
3961 case HI_STYLE_DEF: send_list = def_list; break;
3962 case HI_STYLE_ZOOT: send_list = def_list; break;
3966 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3968 for(uData = channel->channel_info->users; uData; uData = uData->next)
3970 if((uData->access < lowest)
3971 || (uData->access > highest)
3972 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3974 lData.users[matches++] = uData;
3976 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3978 lData.table.length = matches+1;
3979 lData.table.width = 4;
3980 lData.table.flags = TABLE_NO_FREE;
3981 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3982 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3983 lData.table.contents[0] = ary;
3986 ary[2] = "Last Seen";
3988 for(matches = 1; matches < lData.table.length; ++matches)
3990 char seen[INTERVALLEN];
3992 uData = lData.users[matches-1];
3993 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3994 lData.table.contents[matches] = ary;
3995 ary[0] = strtab(uData->access);
3996 ary[1] = uData->handle->handle;
3999 else if(!uData->seen)
4002 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4003 ary[2] = strdup(ary[2]);
4004 if(IsUserSuspended(uData))
4005 ary[3] = "Suspended";
4006 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4007 ary[3] = "Vacation";
4008 else if(HANDLE_FLAGGED(uData->handle, BOT))
4014 for(matches = 1; matches < lData.table.length; ++matches)
4016 free((char*)lData.table.contents[matches][2]);
4017 free(lData.table.contents[matches]);
4019 free(lData.table.contents[0]);
4020 free(lData.table.contents);
4024 static CHANSERV_FUNC(cmd_users)
4026 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4029 static CHANSERV_FUNC(cmd_wlist)
4031 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4034 static CHANSERV_FUNC(cmd_clist)
4036 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4039 static CHANSERV_FUNC(cmd_mlist)
4041 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4044 static CHANSERV_FUNC(cmd_olist)
4046 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4049 static CHANSERV_FUNC(cmd_plist)
4051 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4054 static CHANSERV_FUNC(cmd_bans)
4056 struct userNode *search_u = NULL;
4057 struct helpfile_table tbl;
4058 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4059 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4060 const char *msg_never, *triggered, *expires;
4061 struct banData *ban, **bans;
4065 else if(strchr(search = argv[1], '!'))
4068 search_wilds = search[strcspn(search, "?*")];
4070 else if(!(search_u = GetUserH(search)))
4071 reply("MSG_NICK_UNKNOWN", search);
4073 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4075 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4079 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4084 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4087 bans[matches++] = ban;
4092 tbl.length = matches + 1;
4093 tbl.width = 4 + timed;
4095 tbl.flags = TABLE_NO_FREE;
4096 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4097 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4098 tbl.contents[0][0] = "Mask";
4099 tbl.contents[0][1] = "Set By";
4100 tbl.contents[0][2] = "Triggered";
4103 tbl.contents[0][3] = "Expires";
4104 tbl.contents[0][4] = "Reason";
4107 tbl.contents[0][3] = "Reason";
4110 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4112 free(tbl.contents[0]);
4117 msg_never = user_find_message(user, "MSG_NEVER");
4118 for(ii = 0; ii < matches; )
4124 else if(ban->expires)
4125 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4127 expires = msg_never;
4130 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4132 triggered = msg_never;
4134 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4135 tbl.contents[ii][0] = ban->mask;
4136 tbl.contents[ii][1] = ban->owner;
4137 tbl.contents[ii][2] = strdup(triggered);
4140 tbl.contents[ii][3] = strdup(expires);
4141 tbl.contents[ii][4] = ban->reason;
4144 tbl.contents[ii][3] = ban->reason;
4146 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4147 reply("MSG_MATCH_COUNT", matches);
4148 for(ii = 1; ii < tbl.length; ++ii)
4150 free((char*)tbl.contents[ii][2]);
4152 free((char*)tbl.contents[ii][3]);
4153 free(tbl.contents[ii]);
4155 free(tbl.contents[0]);
4161 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4163 struct chanData *cData = channel->channel_info;
4164 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4166 if(cData->topic_mask)
4167 return !match_ircglob(new_topic, cData->topic_mask);
4168 else if(cData->topic)
4169 return irccasecmp(new_topic, cData->topic);
4174 static CHANSERV_FUNC(cmd_topic)
4176 struct chanData *cData;
4179 cData = channel->channel_info;
4184 SetChannelTopic(channel, chanserv, cData->topic, 1);
4185 reply("CSMSG_TOPIC_SET", cData->topic);
4189 reply("CSMSG_NO_TOPIC", channel->name);
4193 topic = unsplit_string(argv + 1, argc - 1, NULL);
4194 /* If they say "!topic *", use an empty topic. */
4195 if((topic[0] == '*') && (topic[1] == 0))
4197 if(bad_topic(channel, user, topic))
4199 char *topic_mask = cData->topic_mask;
4202 char new_topic[TOPICLEN+1], tchar;
4203 int pos=0, starpos=-1, dpos=0, len;
4205 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4212 len = strlen(topic);
4213 if((dpos + len) > TOPICLEN)
4214 len = TOPICLEN + 1 - dpos;
4215 memcpy(new_topic+dpos, topic, len);
4219 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4220 default: new_topic[dpos++] = tchar; break;
4223 if((dpos > TOPICLEN) || tchar)
4226 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4227 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4230 new_topic[dpos] = 0;
4231 SetChannelTopic(channel, chanserv, new_topic, 1);
4233 reply("CSMSG_TOPIC_LOCKED", channel->name);
4238 SetChannelTopic(channel, chanserv, topic, 1);
4240 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4242 /* Grab the topic and save it as the default topic. */
4244 cData->topic = strdup(channel->topic);
4250 static CHANSERV_FUNC(cmd_mode)
4252 struct userData *uData;
4253 struct mod_chanmode *change;
4259 change = &channel->channel_info->modes;
4260 if(change->modes_set || change->modes_clear) {
4261 modcmd_chanmode_announce(change);
4262 reply("CSMSG_DEFAULTED_MODES", channel->name);
4264 reply("CSMSG_NO_MODES", channel->name);
4268 uData = GetChannelUser(channel->channel_info, user->handle_info);
4270 base_oplevel = MAXOPLEVEL;
4271 else if (uData->access >= UL_OWNER)
4274 base_oplevel = 1 + UL_OWNER - uData->access;
4275 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4278 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4282 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4283 && mode_lock_violated(&channel->channel_info->modes, change))
4286 mod_chanmode_format(&channel->channel_info->modes, modes);
4287 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4291 modcmd_chanmode_announce(change);
4292 mod_chanmode_format(change, fmt);
4293 mod_chanmode_free(change);
4294 reply("CSMSG_MODES_SET", fmt);
4299 chanserv_del_invite_mark(void *data)
4301 struct ChanUser *chanuser = data;
4302 struct chanNode *channel = chanuser->chan;
4304 if(!channel) return;
4305 for(i = 0; i < channel->invited.used; i++)
4307 if(channel->invited.list[i] == chanuser->user) {
4308 userList_remove(&channel->invited, chanuser->user);
4314 static CHANSERV_FUNC(cmd_invite)
4316 struct userData *uData;
4317 struct userNode *invite;
4318 struct ChanUser *chanuser;
4321 uData = GetChannelUser(channel->channel_info, user->handle_info);
4325 if(!(invite = GetUserH(argv[1])))
4327 reply("MSG_NICK_UNKNOWN", argv[1]);
4334 if(GetUserMode(channel, invite))
4336 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4340 for(i = 0; i < channel->invited.used; i++)
4342 if(channel->invited.list[i] == invite) {
4343 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4352 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4353 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4356 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4358 irc_invite(chanserv, invite, channel);
4360 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4362 userList_append(&channel->invited, invite);
4363 chanuser = calloc(1, sizeof(*chanuser));
4364 chanuser->user=invite;
4365 chanuser->chan=channel;
4366 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4371 static CHANSERV_FUNC(cmd_inviteme)
4373 if(GetUserMode(channel, user))
4375 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4378 if(channel->channel_info
4379 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4381 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4384 irc_invite(cmd->parent->bot, user, channel);
4389 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4392 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4394 /* We display things based on two dimensions:
4395 * - Issue time: present or absent
4396 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4397 * (in order of precedence, so something both expired and revoked
4398 * only counts as revoked)
4400 combo = (suspended->issued ? 4 : 0)
4401 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4403 case 0: /* no issue time, indefinite expiration */
4404 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4406 case 1: /* no issue time, expires in future */
4407 intervalString(buf1, suspended->expires-now, user->handle_info);
4408 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4410 case 2: /* no issue time, expired */
4411 intervalString(buf1, now-suspended->expires, user->handle_info);
4412 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4414 case 3: /* no issue time, revoked */
4415 intervalString(buf1, now-suspended->revoked, user->handle_info);
4416 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4418 case 4: /* issue time set, indefinite expiration */
4419 intervalString(buf1, now-suspended->issued, user->handle_info);
4420 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4422 case 5: /* issue time set, expires in future */
4423 intervalString(buf1, now-suspended->issued, user->handle_info);
4424 intervalString(buf2, suspended->expires-now, user->handle_info);
4425 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4427 case 6: /* issue time set, expired */
4428 intervalString(buf1, now-suspended->issued, user->handle_info);
4429 intervalString(buf2, now-suspended->expires, user->handle_info);
4430 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4432 case 7: /* issue time set, revoked */
4433 intervalString(buf1, now-suspended->issued, user->handle_info);
4434 intervalString(buf2, now-suspended->revoked, user->handle_info);
4435 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4438 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4443 static CHANSERV_FUNC(cmd_info)
4445 char modes[MAXLEN], buffer[INTERVALLEN];
4446 struct userData *uData, *owner;
4447 struct chanData *cData;
4448 struct do_not_register *dnr;
4453 cData = channel->channel_info;
4454 reply("CSMSG_CHANNEL_INFO", channel->name);
4456 uData = GetChannelUser(cData, user->handle_info);
4457 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4459 mod_chanmode_format(&cData->modes, modes);
4460 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4461 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4464 for(it = dict_first(cData->notes); it; it = iter_next(it))
4468 note = iter_data(it);
4469 if(!note_type_visible_to_user(cData, note->type, user))
4472 padding = PADLEN - 1 - strlen(iter_key(it));
4473 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4476 reply("CSMSG_CHANNEL_MAX", cData->max);
4477 for(owner = cData->users; owner; owner = owner->next)
4478 if(owner->access == UL_OWNER)
4479 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4480 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4481 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4482 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4484 privileged = IsStaff(user);
4486 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4487 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4488 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4490 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4491 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4493 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4495 struct suspended *suspended;
4496 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4497 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4498 show_suspension_info(cmd, user, suspended);
4500 else if(IsSuspended(cData))
4502 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4503 show_suspension_info(cmd, user, cData->suspended);
4508 static CHANSERV_FUNC(cmd_netinfo)
4510 extern unsigned long boot_time;
4511 extern unsigned long burst_length;
4512 char interval[INTERVALLEN];
4514 reply("CSMSG_NETWORK_INFO");
4515 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4516 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4517 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4518 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4519 reply("CSMSG_NETWORK_BANS", banCount);
4520 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4521 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4522 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4527 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4529 struct helpfile_table table;
4531 struct userNode *user;
4536 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4537 table.contents = alloca(list->used*sizeof(*table.contents));
4538 for(nn=0; nn<list->used; nn++)
4540 user = list->list[nn];
4541 if(user->modes & skip_flags)
4545 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4548 nick = alloca(strlen(user->nick)+3);
4549 sprintf(nick, "(%s)", user->nick);
4553 table.contents[table.length][0] = nick;
4556 table_send(chanserv, to->nick, 0, NULL, table);
4559 static CHANSERV_FUNC(cmd_ircops)
4561 reply("CSMSG_STAFF_OPERS");
4562 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4566 static CHANSERV_FUNC(cmd_helpers)
4568 reply("CSMSG_STAFF_HELPERS");
4569 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4573 static CHANSERV_FUNC(cmd_staff)
4575 reply("CSMSG_NETWORK_STAFF");
4576 cmd_ircops(CSFUNC_ARGS);
4577 cmd_helpers(CSFUNC_ARGS);
4581 static CHANSERV_FUNC(cmd_peek)
4583 struct modeNode *mn;
4584 char modes[MODELEN];
4586 struct helpfile_table table;
4587 int opcount = 0, voicecount = 0, srvcount = 0;
4589 irc_make_chanmode(channel, modes);
4591 reply("CSMSG_PEEK_INFO", channel->name);
4592 reply("CSMSG_PEEK_TOPIC", channel->topic);
4593 reply("CSMSG_PEEK_MODES", modes);
4597 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4598 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4599 for(n = 0; n < channel->members.used; n++)
4601 mn = channel->members.list[n];
4602 if(IsLocal(mn->user))
4604 else if(mn->modes & MODE_CHANOP)
4606 else if(mn->modes & MODE_VOICE)
4609 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4611 table.contents[table.length] = alloca(sizeof(**table.contents));
4612 table.contents[table.length][0] = mn->user->nick;
4616 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4617 (channel->members.used - opcount - voicecount - srvcount));
4621 reply("CSMSG_PEEK_OPS");
4622 table_send(chanserv, user->nick, 0, NULL, table);
4625 reply("CSMSG_PEEK_NO_OPS");
4629 static MODCMD_FUNC(cmd_wipeinfo)
4631 struct handle_info *victim;
4632 struct userData *ud, *actor, *real_actor;
4633 unsigned int override = 0;
4636 actor = GetChannelUser(channel->channel_info, user->handle_info);
4637 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4638 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4640 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4642 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4645 if((ud->access >= actor->access) && (ud != actor))
4647 reply("MSG_USER_OUTRANKED", victim->handle);
4650 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4651 override = CMD_LOG_OVERRIDE;
4655 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4656 return 1 | override;
4659 static CHANSERV_FUNC(cmd_resync)
4661 struct mod_chanmode *changes;
4662 struct chanData *cData = channel->channel_info;
4663 unsigned int ii, used;
4665 changes = mod_chanmode_alloc(channel->members.used * 2);
4666 for(ii = used = 0; ii < channel->members.used; ++ii)
4668 struct modeNode *mn = channel->members.list[ii];
4669 struct userData *uData;
4671 if(IsService(mn->user))
4674 uData = GetChannelAccess(cData, mn->user->handle_info);
4675 if(!cData->lvlOpts[lvlGiveOps]
4676 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4678 if(!(mn->modes & MODE_CHANOP))
4680 changes->args[used].mode = MODE_CHANOP;
4681 changes->args[used++].u.member = mn;
4684 else if(!cData->lvlOpts[lvlGiveVoice]
4685 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4687 if(mn->modes & MODE_CHANOP)
4689 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4690 changes->args[used++].u.member = mn;
4692 if(!(mn->modes & MODE_VOICE))
4694 changes->args[used].mode = MODE_VOICE;
4695 changes->args[used++].u.member = mn;
4702 changes->args[used].mode = MODE_REMOVE | mn->modes;
4703 changes->args[used++].u.member = mn;
4707 changes->argc = used;
4708 modcmd_chanmode_announce(changes);
4709 mod_chanmode_free(changes);
4710 reply("CSMSG_RESYNCED_USERS", channel->name);
4714 static CHANSERV_FUNC(cmd_seen)
4716 struct userData *uData;
4717 struct handle_info *handle;
4718 char seen[INTERVALLEN];
4722 if(!irccasecmp(argv[1], chanserv->nick))
4724 reply("CSMSG_IS_CHANSERV");
4728 if(!(handle = get_handle_info(argv[1])))
4730 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4734 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4736 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4741 reply("CSMSG_USER_PRESENT", handle->handle);
4742 else if(uData->seen)
4743 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4745 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4747 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4748 reply("CSMSG_USER_VACATION", handle->handle);
4753 static MODCMD_FUNC(cmd_names)
4755 struct userNode *targ;
4756 struct userData *targData;
4757 unsigned int ii, pos;
4760 for(ii=pos=0; ii<channel->members.used; ++ii)
4762 targ = channel->members.list[ii]->user;
4763 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4766 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4769 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4773 if(IsUserSuspended(targData))
4775 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4778 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4779 reply("CSMSG_END_NAMES", channel->name);
4784 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4786 switch(ntype->visible_type)
4788 case NOTE_VIS_ALL: return 1;
4789 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4790 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4795 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4797 struct userData *uData;
4799 switch(ntype->set_access_type)
4801 case NOTE_SET_CHANNEL_ACCESS:
4802 if(!user->handle_info)
4804 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4806 return uData->access >= ntype->set_access.min_ulevel;
4807 case NOTE_SET_CHANNEL_SETTER:
4808 return check_user_level(channel, user, lvlSetters, 1, 0);
4809 case NOTE_SET_PRIVILEGED: default:
4810 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4814 static CHANSERV_FUNC(cmd_note)
4816 struct chanData *cData;
4818 struct note_type *ntype;
4820 cData = channel->channel_info;
4823 reply("CSMSG_NOT_REGISTERED", channel->name);
4827 /* If no arguments, show all visible notes for the channel. */
4833 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4835 note = iter_data(it);
4836 if(!note_type_visible_to_user(cData, note->type, user))
4839 reply("CSMSG_NOTELIST_HEADER", channel->name);
4840 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4843 reply("CSMSG_NOTELIST_END", channel->name);
4845 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4847 /* If one argument, show the named note. */
4850 if((note = dict_find(cData->notes, argv[1], NULL))
4851 && note_type_visible_to_user(cData, note->type, user))
4853 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4855 else if((ntype = dict_find(note_types, argv[1], NULL))
4856 && note_type_visible_to_user(NULL, ntype, user))
4858 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4863 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4867 /* Assume they're trying to set a note. */
4871 ntype = dict_find(note_types, argv[1], NULL);
4874 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4877 else if(note_type_settable_by_user(channel, ntype, user))
4879 note_text = unsplit_string(argv+2, argc-2, NULL);
4880 if((note = dict_find(cData->notes, argv[1], NULL)))
4881 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4882 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4883 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4885 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4887 /* The note is viewable to staff only, so return 0
4888 to keep the invocation from getting logged (or
4889 regular users can see it in !events). */
4895 reply("CSMSG_NO_ACCESS");
4902 static CHANSERV_FUNC(cmd_delnote)
4907 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4908 || !note_type_settable_by_user(channel, note->type, user))
4910 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4913 dict_remove(channel->channel_info->notes, note->type->name);
4914 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4918 static CHANSERV_FUNC(cmd_events)
4920 struct logSearch discrim;
4921 struct logReport report;
4922 unsigned int matches, limit;
4924 limit = (argc > 1) ? atoi(argv[1]) : 10;
4925 if(limit < 1 || limit > 200)
4928 memset(&discrim, 0, sizeof(discrim));
4929 discrim.masks.bot = chanserv;
4930 discrim.masks.channel_name = channel->name;
4932 discrim.masks.command = argv[2];
4933 discrim.limit = limit;
4934 discrim.max_time = INT_MAX;
4935 discrim.severities = 1 << LOG_COMMAND;
4936 report.reporter = chanserv;
4938 reply("CSMSG_EVENT_SEARCH_RESULTS");
4939 matches = log_entry_search(&discrim, log_report_entry, &report);
4941 reply("MSG_MATCH_COUNT", matches);
4943 reply("MSG_NO_MATCHES");
4947 static CHANSERV_FUNC(cmd_say)
4953 msg = unsplit_string(argv + 1, argc - 1, NULL);
4954 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4956 else if(*argv[1] == '*' && argv[1][1] != '\0')
4958 struct handle_info *hi;
4959 struct userNode *authed;
4962 msg = unsplit_string(argv + 2, argc - 2, NULL);
4964 if (!(hi = get_handle_info(argv[1] + 1)))
4966 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4970 for (authed = hi->users; authed; authed = authed->next_authed)
4971 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4973 else if(GetUserH(argv[1]))
4976 msg = unsplit_string(argv + 2, argc - 2, NULL);
4977 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4981 reply("MSG_NOT_TARGET_NAME");
4987 static CHANSERV_FUNC(cmd_emote)
4993 /* CTCP is so annoying. */
4994 msg = unsplit_string(argv + 1, argc - 1, NULL);
4995 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4997 else if(*argv[1] == '*' && argv[1][1] != '\0')
4999 struct handle_info *hi;
5000 struct userNode *authed;
5003 msg = unsplit_string(argv + 2, argc - 2, NULL);
5005 if (!(hi = get_handle_info(argv[1] + 1)))
5007 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5011 for (authed = hi->users; authed; authed = authed->next_authed)
5012 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5014 else if(GetUserH(argv[1]))
5016 msg = unsplit_string(argv + 2, argc - 2, NULL);
5017 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5021 reply("MSG_NOT_TARGET_NAME");
5027 struct channelList *
5028 chanserv_support_channels(void)
5030 return &chanserv_conf.support_channels;
5033 static CHANSERV_FUNC(cmd_expire)
5035 int channel_count = registered_channels;
5036 expire_channels(NULL);
5037 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5042 chanserv_expire_suspension(void *data)
5044 struct suspended *suspended = data;
5045 struct chanNode *channel;
5048 /* Update the channel registration data structure. */
5049 if(!suspended->expires || (now < suspended->expires))
5050 suspended->revoked = now;
5051 channel = suspended->cData->channel;
5052 suspended->cData->channel = channel;
5053 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5055 /* If appropriate, re-join ChanServ to the channel. */
5056 if(!IsOffChannel(suspended->cData))
5058 spamserv_cs_suspend(channel, 0, 0, NULL);
5059 ss_cs_join_channel(channel, 1);
5062 /* Mark everyone currently in the channel as present. */
5063 for(ii = 0; ii < channel->members.used; ++ii)
5065 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5074 static CHANSERV_FUNC(cmd_csuspend)
5076 struct suspended *suspended;
5077 char reason[MAXLEN];
5078 unsigned long expiry, duration;
5079 struct userData *uData;
5083 if(IsProtected(channel->channel_info))
5085 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5089 if(argv[1][0] == '!')
5091 else if(IsSuspended(channel->channel_info))
5093 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5094 show_suspension_info(cmd, user, channel->channel_info->suspended);
5098 if(!strcmp(argv[1], "0"))
5100 else if((duration = ParseInterval(argv[1])))
5101 expiry = now + duration;
5104 reply("MSG_INVALID_DURATION", argv[1]);
5108 unsplit_string(argv + 2, argc - 2, reason);
5110 suspended = calloc(1, sizeof(*suspended));
5111 suspended->revoked = 0;
5112 suspended->issued = now;
5113 suspended->suspender = strdup(user->handle_info->handle);
5114 suspended->expires = expiry;
5115 suspended->reason = strdup(reason);
5116 suspended->cData = channel->channel_info;
5117 suspended->previous = suspended->cData->suspended;
5118 suspended->cData->suspended = suspended;
5120 if(suspended->expires)
5121 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5123 if(IsSuspended(channel->channel_info))
5125 suspended->previous->revoked = now;
5126 if(suspended->previous->expires)
5127 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5128 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5129 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5133 /* Mark all users in channel as absent. */
5134 for(uData = channel->channel_info->users; uData; uData = uData->next)
5143 /* Mark the channel as suspended, then part. */
5144 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5145 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5146 DelChannelUser(chanserv, channel, suspended->reason, 0);
5147 reply("CSMSG_SUSPENDED", channel->name);
5148 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5149 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5154 static CHANSERV_FUNC(cmd_cunsuspend)
5156 struct suspended *suspended;
5157 char message[MAXLEN];
5159 if(!IsSuspended(channel->channel_info))
5161 reply("CSMSG_NOT_SUSPENDED", channel->name);
5165 suspended = channel->channel_info->suspended;
5167 /* Expire the suspension and join ChanServ to the channel. */
5168 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5169 chanserv_expire_suspension(suspended);
5170 reply("CSMSG_UNSUSPENDED", channel->name);
5171 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5172 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5176 typedef struct chanservSearch
5181 unsigned long unvisited;
5182 unsigned long registered;
5184 unsigned long flags;
5188 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5191 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5196 search = malloc(sizeof(struct chanservSearch));
5197 memset(search, 0, sizeof(*search));
5200 for(i = 0; i < argc; i++)
5202 /* Assume all criteria require arguments. */
5205 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5209 if(!irccasecmp(argv[i], "name"))
5210 search->name = argv[++i];
5211 else if(!irccasecmp(argv[i], "registrar"))
5212 search->registrar = argv[++i];
5213 else if(!irccasecmp(argv[i], "unvisited"))
5214 search->unvisited = ParseInterval(argv[++i]);
5215 else if(!irccasecmp(argv[i], "registered"))
5216 search->registered = ParseInterval(argv[++i]);
5217 else if(!irccasecmp(argv[i], "flags"))
5220 if(!irccasecmp(argv[i], "nodelete"))
5221 search->flags |= CHANNEL_NODELETE;
5222 else if(!irccasecmp(argv[i], "suspended"))
5223 search->flags |= CHANNEL_SUSPENDED;
5224 else if(!irccasecmp(argv[i], "unreviewed"))
5225 search->flags |= CHANNEL_UNREVIEWED;
5228 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5232 else if(!irccasecmp(argv[i], "limit"))
5233 search->limit = strtoul(argv[++i], NULL, 10);
5236 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5241 if(search->name && !strcmp(search->name, "*"))
5243 if(search->registrar && !strcmp(search->registrar, "*"))
5244 search->registrar = 0;
5253 chanserv_channel_match(struct chanData *channel, search_t search)
5255 const char *name = channel->channel->name;
5256 if((search->name && !match_ircglob(name, search->name)) ||
5257 (search->registrar && !channel->registrar) ||
5258 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5259 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5260 (search->registered && (now - channel->registered) > search->registered) ||
5261 (search->flags && ((search->flags & channel->flags) != search->flags)))
5268 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5270 struct chanData *channel;
5271 unsigned int matches = 0;
5273 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5275 if(!chanserv_channel_match(channel, search))
5285 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5290 search_print(struct chanData *channel, void *data)
5292 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5295 static CHANSERV_FUNC(cmd_search)
5298 unsigned int matches;
5299 channel_search_func action;
5303 if(!irccasecmp(argv[1], "count"))
5304 action = search_count;
5305 else if(!irccasecmp(argv[1], "print"))
5306 action = search_print;
5309 reply("CSMSG_ACTION_INVALID", argv[1]);
5313 search = chanserv_search_create(user, argc - 2, argv + 2);
5317 if(action == search_count)
5318 search->limit = INT_MAX;
5320 if(action == search_print)
5321 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5323 matches = chanserv_channel_search(search, action, user);
5326 reply("MSG_MATCH_COUNT", matches);
5328 reply("MSG_NO_MATCHES");
5334 static CHANSERV_FUNC(cmd_unvisited)
5336 struct chanData *cData;
5337 unsigned long interval = chanserv_conf.channel_expire_delay;
5338 char buffer[INTERVALLEN];
5339 unsigned int limit = 25, matches = 0;
5343 interval = ParseInterval(argv[1]);
5345 limit = atoi(argv[2]);
5348 intervalString(buffer, interval, user->handle_info);
5349 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5351 for(cData = channelList; cData && matches < limit; cData = cData->next)
5353 if((now - cData->visited) < interval)
5356 intervalString(buffer, now - cData->visited, user->handle_info);
5357 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5364 static MODCMD_FUNC(chan_opt_defaulttopic)
5370 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5372 reply("CSMSG_TOPIC_LOCKED", channel->name);
5376 topic = unsplit_string(argv+1, argc-1, NULL);
5378 free(channel->channel_info->topic);
5379 if(topic[0] == '*' && topic[1] == 0)
5381 topic = channel->channel_info->topic = NULL;
5385 topic = channel->channel_info->topic = strdup(topic);
5386 if(channel->channel_info->topic_mask
5387 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5388 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5390 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5393 if(channel->channel_info->topic)
5394 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5396 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5400 static MODCMD_FUNC(chan_opt_topicmask)
5404 struct chanData *cData = channel->channel_info;
5407 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5409 reply("CSMSG_TOPIC_LOCKED", channel->name);
5413 mask = unsplit_string(argv+1, argc-1, NULL);
5415 if(cData->topic_mask)
5416 free(cData->topic_mask);
5417 if(mask[0] == '*' && mask[1] == 0)
5419 cData->topic_mask = 0;
5423 cData->topic_mask = strdup(mask);
5425 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5426 else if(!match_ircglob(cData->topic, cData->topic_mask))
5427 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5431 if(channel->channel_info->topic_mask)
5432 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5434 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5438 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5442 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5446 if(greeting[0] == '*' && greeting[1] == 0)
5450 unsigned int length = strlen(greeting);
5451 if(length > chanserv_conf.greeting_length)
5453 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5456 *data = strdup(greeting);
5465 reply(name, user_find_message(user, "MSG_NONE"));
5469 static MODCMD_FUNC(chan_opt_greeting)
5471 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5474 static MODCMD_FUNC(chan_opt_usergreeting)
5476 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5479 static MODCMD_FUNC(chan_opt_modes)
5481 struct mod_chanmode *new_modes;
5486 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5488 reply("CSMSG_NO_ACCESS");
5491 if(argv[1][0] == '*' && argv[1][1] == 0)
5493 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5495 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5497 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5500 else if(new_modes->argc > 1)
5502 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5503 mod_chanmode_free(new_modes);
5508 channel->channel_info->modes = *new_modes;
5509 modcmd_chanmode_announce(new_modes);
5510 mod_chanmode_free(new_modes);
5514 mod_chanmode_format(&channel->channel_info->modes, modes);
5516 reply("CSMSG_SET_MODES", modes);
5518 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5522 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5524 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5526 struct chanData *cData = channel->channel_info;
5531 /* Set flag according to value. */
5532 if(enabled_string(argv[1]))
5534 cData->flags |= mask;
5537 else if(disabled_string(argv[1]))
5539 cData->flags &= ~mask;
5544 reply("MSG_INVALID_BINARY", argv[1]);
5550 /* Find current option value. */
5551 value = (cData->flags & mask) ? 1 : 0;
5555 reply(name, user_find_message(user, "MSG_ON"));
5557 reply(name, user_find_message(user, "MSG_OFF"));
5561 static MODCMD_FUNC(chan_opt_nodelete)
5563 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5565 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5569 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5572 static MODCMD_FUNC(chan_opt_dynlimit)
5574 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5577 static MODCMD_FUNC(chan_opt_offchannel)
5579 struct chanData *cData = channel->channel_info;
5584 /* Set flag according to value. */
5585 if(enabled_string(argv[1]))
5587 if(!IsOffChannel(cData))
5588 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5589 cData->flags |= CHANNEL_OFFCHANNEL;
5592 else if(disabled_string(argv[1]))
5594 if(IsOffChannel(cData))
5596 struct mod_chanmode change;
5597 mod_chanmode_init(&change);
5599 change.args[0].mode = MODE_CHANOP;
5600 change.args[0].u.member = AddChannelUser(chanserv, channel);
5601 mod_chanmode_announce(chanserv, channel, &change);
5603 cData->flags &= ~CHANNEL_OFFCHANNEL;
5608 reply("MSG_INVALID_BINARY", argv[1]);
5614 /* Find current option value. */
5615 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5619 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5621 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5625 static MODCMD_FUNC(chan_opt_unreviewed)
5627 struct chanData *cData = channel->channel_info;
5628 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5634 /* The two directions can have different ACLs. */
5635 if(enabled_string(argv[1]))
5637 else if(disabled_string(argv[1]))
5641 reply("MSG_INVALID_BINARY", argv[1]);
5645 if (new_value != value)
5647 struct svccmd *subcmd;
5648 char subcmd_name[32];
5650 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5651 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5654 reply("MSG_COMMAND_DISABLED", subcmd_name);
5657 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5661 cData->flags |= CHANNEL_UNREVIEWED;
5664 free(cData->registrar);
5665 cData->registrar = strdup(user->handle_info->handle);
5666 cData->flags &= ~CHANNEL_UNREVIEWED;
5673 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5675 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5679 static MODCMD_FUNC(chan_opt_defaults)
5681 struct userData *uData;
5682 struct chanData *cData;
5683 const char *confirm;
5684 enum levelOption lvlOpt;
5685 enum charOption chOpt;
5687 cData = channel->channel_info;
5688 uData = GetChannelUser(cData, user->handle_info);
5689 if(!uData || (uData->access < UL_OWNER))
5691 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5694 confirm = make_confirmation_string(uData);
5695 if((argc < 2) || strcmp(argv[1], confirm))
5697 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5700 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5701 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5702 cData->modes = chanserv_conf.default_modes;
5703 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5704 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5705 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5706 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5707 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5712 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5714 struct chanData *cData = channel->channel_info;
5715 struct userData *uData;
5716 unsigned short value;
5720 if(!check_user_level(channel, user, option, 1, 1))
5722 reply("CSMSG_CANNOT_SET");
5725 value = user_level_from_name(argv[1], UL_OWNER+1);
5726 if(!value && strcmp(argv[1], "0"))
5728 reply("CSMSG_INVALID_ACCESS", argv[1]);
5731 uData = GetChannelUser(cData, user->handle_info);
5732 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5734 reply("CSMSG_BAD_SETLEVEL");
5740 if(value > cData->lvlOpts[lvlGiveOps])
5742 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5747 if(value < cData->lvlOpts[lvlGiveVoice])
5749 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5754 /* This test only applies to owners, since non-owners
5755 * trying to set an option to above their level get caught
5756 * by the CSMSG_BAD_SETLEVEL test above.
5758 if(value > uData->access)
5760 reply("CSMSG_BAD_SETTERS");
5767 cData->lvlOpts[option] = value;
5769 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5773 static MODCMD_FUNC(chan_opt_enfops)
5775 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5778 static MODCMD_FUNC(chan_opt_giveops)
5780 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5783 static MODCMD_FUNC(chan_opt_enfmodes)
5785 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5788 static MODCMD_FUNC(chan_opt_enftopic)
5790 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5793 static MODCMD_FUNC(chan_opt_pubcmd)
5795 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5798 static MODCMD_FUNC(chan_opt_setters)
5800 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5803 static MODCMD_FUNC(chan_opt_ctcpusers)
5805 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5808 static MODCMD_FUNC(chan_opt_userinfo)
5810 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5813 static MODCMD_FUNC(chan_opt_givevoice)
5815 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5818 static MODCMD_FUNC(chan_opt_topicsnarf)
5820 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5823 static MODCMD_FUNC(chan_opt_inviteme)
5825 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5829 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5831 struct chanData *cData = channel->channel_info;
5832 int count = charOptions[option].count, idx;
5836 idx = atoi(argv[1]);
5838 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5840 reply("CSMSG_INVALID_NUMERIC", idx);
5841 /* Show possible values. */
5842 for(idx = 0; idx < count; idx++)
5843 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5847 cData->chOpts[option] = charOptions[option].values[idx].value;
5851 /* Find current option value. */
5854 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5858 /* Somehow, the option value is corrupt; reset it to the default. */
5859 cData->chOpts[option] = charOptions[option].default_value;
5864 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5868 static MODCMD_FUNC(chan_opt_protect)
5870 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5873 static MODCMD_FUNC(chan_opt_toys)
5875 return channel_multiple_option(chToys, CSFUNC_ARGS);
5878 static MODCMD_FUNC(chan_opt_ctcpreaction)
5880 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5883 static MODCMD_FUNC(chan_opt_topicrefresh)
5885 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5888 static struct svccmd_list set_shows_list;
5891 handle_svccmd_unbind(struct svccmd *target) {
5893 for(ii=0; ii<set_shows_list.used; ++ii)
5894 if(target == set_shows_list.list[ii])
5895 set_shows_list.used = 0;
5898 static CHANSERV_FUNC(cmd_set)
5900 struct svccmd *subcmd;
5904 /* Check if we need to (re-)initialize set_shows_list. */
5905 if(!set_shows_list.used)
5907 if(!set_shows_list.size)
5909 set_shows_list.size = chanserv_conf.set_shows->used;
5910 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5912 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5914 const char *name = chanserv_conf.set_shows->list[ii];
5915 sprintf(buf, "%s %s", argv[0], name);
5916 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5919 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5922 svccmd_list_append(&set_shows_list, subcmd);
5928 reply("CSMSG_CHANNEL_OPTIONS");
5929 for(ii = 0; ii < set_shows_list.used; ii++)
5931 subcmd = set_shows_list.list[ii];
5932 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5937 sprintf(buf, "%s %s", argv[0], argv[1]);
5938 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5941 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5944 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5946 reply("CSMSG_NO_ACCESS");
5952 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5956 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5958 struct userData *uData;
5960 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5963 reply("CSMSG_NOT_USER", channel->name);
5969 /* Just show current option value. */
5971 else if(enabled_string(argv[1]))
5973 uData->flags |= mask;
5975 else if(disabled_string(argv[1]))
5977 uData->flags &= ~mask;
5981 reply("MSG_INVALID_BINARY", argv[1]);
5985 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5989 static MODCMD_FUNC(user_opt_noautoop)
5991 struct userData *uData;
5993 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5996 reply("CSMSG_NOT_USER", channel->name);
5999 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6000 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6002 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6005 static MODCMD_FUNC(user_opt_autoinvite)
6007 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6009 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6011 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6014 static MODCMD_FUNC(user_opt_info)
6016 struct userData *uData;
6019 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6023 /* If they got past the command restrictions (which require access)
6024 * but fail this test, we have some fool with security override on.
6026 reply("CSMSG_NOT_USER", channel->name);
6033 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6034 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6036 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6039 bp = strcspn(infoline, "\001");
6042 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6047 if(infoline[0] == '*' && infoline[1] == 0)
6050 uData->info = strdup(infoline);
6053 reply("CSMSG_USET_INFO", uData->info);
6055 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6059 struct svccmd_list uset_shows_list;
6061 static CHANSERV_FUNC(cmd_uset)
6063 struct svccmd *subcmd;
6067 /* Check if we need to (re-)initialize uset_shows_list. */
6068 if(!uset_shows_list.used)
6072 "NoAutoOp", "AutoInvite", "Info"
6075 if(!uset_shows_list.size)
6077 uset_shows_list.size = ArrayLength(options);
6078 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6080 for(ii = 0; ii < ArrayLength(options); ii++)
6082 const char *name = options[ii];
6083 sprintf(buf, "%s %s", argv[0], name);
6084 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6087 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6090 svccmd_list_append(&uset_shows_list, subcmd);
6096 /* Do this so options are presented in a consistent order. */
6097 reply("CSMSG_USER_OPTIONS");
6098 for(ii = 0; ii < uset_shows_list.used; ii++)
6099 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6103 sprintf(buf, "%s %s", argv[0], argv[1]);
6104 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6107 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6111 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6114 static CHANSERV_FUNC(cmd_giveownership)
6116 struct handle_info *new_owner_hi;
6117 struct userData *new_owner;
6118 struct userData *curr_user;
6119 struct userData *invoker;
6120 struct chanData *cData = channel->channel_info;
6121 struct do_not_register *dnr;
6122 const char *confirm;
6124 unsigned short co_access;
6125 char reason[MAXLEN];
6128 curr_user = GetChannelAccess(cData, user->handle_info);
6129 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6130 if(!curr_user || (curr_user->access != UL_OWNER))
6132 struct userData *owner = NULL;
6133 for(curr_user = channel->channel_info->users;
6135 curr_user = curr_user->next)
6137 if(curr_user->access != UL_OWNER)
6141 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6148 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6150 char delay[INTERVALLEN];
6151 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6152 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6155 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6157 if(new_owner_hi == user->handle_info)
6159 reply("CSMSG_NO_TRANSFER_SELF");
6162 new_owner = GetChannelAccess(cData, new_owner_hi);
6167 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6171 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6175 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6177 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6180 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6181 if(!IsHelping(user))
6182 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6184 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6187 invoker = GetChannelUser(cData, user->handle_info);
6188 if(invoker->access <= UL_OWNER)
6190 confirm = make_confirmation_string(curr_user);
6191 if((argc < 3) || strcmp(argv[2], confirm))
6193 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6197 if(new_owner->access >= UL_COOWNER)
6198 co_access = new_owner->access;
6200 co_access = UL_COOWNER;
6201 new_owner->access = UL_OWNER;
6203 curr_user->access = co_access;
6204 cData->ownerTransfer = now;
6205 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6206 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6207 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6211 static CHANSERV_FUNC(cmd_suspend)
6213 struct handle_info *hi;
6214 struct userData *actor, *real_actor, *target;
6215 unsigned int override = 0;
6218 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6219 actor = GetChannelUser(channel->channel_info, user->handle_info);
6220 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6221 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6223 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6226 if(target->access >= actor->access)
6228 reply("MSG_USER_OUTRANKED", hi->handle);
6231 if(target->flags & USER_SUSPENDED)
6233 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6238 target->present = 0;
6241 if(!real_actor || target->access >= real_actor->access)
6242 override = CMD_LOG_OVERRIDE;
6243 target->flags |= USER_SUSPENDED;
6244 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6245 return 1 | override;
6248 static CHANSERV_FUNC(cmd_unsuspend)
6250 struct handle_info *hi;
6251 struct userData *actor, *real_actor, *target;
6252 unsigned int override = 0;
6255 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6256 actor = GetChannelUser(channel->channel_info, user->handle_info);
6257 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6258 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6260 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6263 if(target->access >= actor->access)
6265 reply("MSG_USER_OUTRANKED", hi->handle);
6268 if(!(target->flags & USER_SUSPENDED))
6270 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6273 if(!real_actor || target->access >= real_actor->access)
6274 override = CMD_LOG_OVERRIDE;
6275 target->flags &= ~USER_SUSPENDED;
6276 scan_user_presence(target, NULL);
6277 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6278 return 1 | override;
6281 static MODCMD_FUNC(cmd_deleteme)
6283 struct handle_info *hi;
6284 struct userData *target;
6285 const char *confirm_string;
6286 unsigned short access_level;
6289 hi = user->handle_info;
6290 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6292 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6295 if(target->access == UL_OWNER)
6297 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6300 confirm_string = make_confirmation_string(target);
6301 if((argc < 2) || strcmp(argv[1], confirm_string))
6303 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6306 access_level = target->access;
6307 channel_name = strdup(channel->name);
6308 del_channel_user(target, 1);
6309 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6315 chanserv_refresh_topics(UNUSED_ARG(void *data))
6317 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6318 struct chanData *cData;
6321 for(cData = channelList; cData; cData = cData->next)
6323 if(IsSuspended(cData))
6325 opt = cData->chOpts[chTopicRefresh];
6328 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6331 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6332 cData->last_refresh = refresh_num;
6334 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6337 static CHANSERV_FUNC(cmd_unf)
6341 char response[MAXLEN];
6342 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6343 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6344 irc_privmsg(cmd->parent->bot, channel->name, response);
6347 reply("CSMSG_UNF_RESPONSE");
6351 static CHANSERV_FUNC(cmd_ping)
6355 char response[MAXLEN];
6356 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6357 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6358 irc_privmsg(cmd->parent->bot, channel->name, response);
6361 reply("CSMSG_PING_RESPONSE");
6365 static CHANSERV_FUNC(cmd_wut)
6369 char response[MAXLEN];
6370 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6371 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6372 irc_privmsg(cmd->parent->bot, channel->name, response);
6375 reply("CSMSG_WUT_RESPONSE");
6379 static CHANSERV_FUNC(cmd_8ball)
6381 unsigned int i, j, accum;
6386 for(i=1; i<argc; i++)
6387 for(j=0; argv[i][j]; j++)
6388 accum = (accum << 5) - accum + toupper(argv[i][j]);
6389 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6392 char response[MAXLEN];
6393 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6394 irc_privmsg(cmd->parent->bot, channel->name, response);
6397 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6401 static CHANSERV_FUNC(cmd_d)
6403 unsigned long sides, count, modifier, ii, total;
6404 char response[MAXLEN], *sep;
6408 if((count = strtoul(argv[1], &sep, 10)) < 1)
6418 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6419 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6423 else if((sep[0] == '-') && isdigit(sep[1]))
6424 modifier = strtoul(sep, NULL, 10);
6425 else if((sep[0] == '+') && isdigit(sep[1]))
6426 modifier = strtoul(sep+1, NULL, 10);
6433 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6438 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6441 for(total = ii = 0; ii < count; ++ii)
6442 total += (rand() % sides) + 1;
6445 if((count > 1) || modifier)
6447 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6448 sprintf(response, fmt, total, count, sides, modifier);
6452 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6453 sprintf(response, fmt, total, sides);
6456 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6458 send_message_type(4, user, cmd->parent->bot, "%s", response);
6462 static CHANSERV_FUNC(cmd_huggle)
6464 /* CTCP must be via PRIVMSG, never notice */
6466 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6468 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6473 chanserv_adjust_limit(void *data)
6475 struct mod_chanmode change;
6476 struct chanData *cData = data;
6477 struct chanNode *channel = cData->channel;
6480 if(IsSuspended(cData))
6483 cData->limitAdjusted = now;
6484 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6485 if(cData->modes.modes_set & MODE_LIMIT)
6487 if(limit > cData->modes.new_limit)
6488 limit = cData->modes.new_limit;
6489 else if(limit == cData->modes.new_limit)
6493 mod_chanmode_init(&change);
6494 change.modes_set = MODE_LIMIT;
6495 change.new_limit = limit;
6496 mod_chanmode_announce(chanserv, channel, &change);
6500 handle_new_channel(struct chanNode *channel)
6502 struct chanData *cData;
6504 if(!(cData = channel->channel_info))
6507 if(cData->modes.modes_set || cData->modes.modes_clear)
6508 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6510 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6511 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6514 /* Welcome to my worst nightmare. Warning: Read (or modify)
6515 the code below at your own risk. */
6517 handle_join(struct modeNode *mNode)
6519 struct mod_chanmode change;
6520 struct userNode *user = mNode->user;
6521 struct chanNode *channel = mNode->channel;
6522 struct chanData *cData;
6523 struct userData *uData = NULL;
6524 struct banData *bData;
6525 struct handle_info *handle;
6526 unsigned int modes = 0, info = 0;
6529 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6532 cData = channel->channel_info;
6533 if(channel->members.used > cData->max)
6534 cData->max = channel->members.used;
6536 for(i = 0; i < channel->invited.used; i++)
6538 if(channel->invited.list[i] == user) {
6539 userList_remove(&channel->invited, user);
6543 /* Check for bans. If they're joining through a ban, one of two
6545 * 1: Join during a netburst, by riding the break. Kick them
6546 * unless they have ops or voice in the channel.
6547 * 2: They're allowed to join through the ban (an invite in
6548 * ircu2.10, or a +e on Hybrid, or something).
6549 * If they're not joining through a ban, and the banlist is not
6550 * full, see if they're on the banlist for the channel. If so,
6553 if(user->uplink->burst && !mNode->modes)
6556 for(ii = 0; ii < channel->banlist.used; ii++)
6558 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6560 /* Riding a netburst. Naughty. */
6561 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6567 mod_chanmode_init(&change);
6569 if(channel->banlist.used < MAXBANS)
6571 /* Not joining through a ban. */
6572 for(bData = cData->bans;
6573 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6574 bData = bData->next);
6578 char kick_reason[MAXLEN];
6579 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6581 bData->triggered = now;
6582 if(bData != cData->bans)
6584 /* Shuffle the ban to the head of the list. */
6586 bData->next->prev = bData->prev;
6588 bData->prev->next = bData->next;
6591 bData->next = cData->bans;
6594 cData->bans->prev = bData;
6595 cData->bans = bData;
6598 change.args[0].mode = MODE_BAN;
6599 change.args[0].u.hostmask = bData->mask;
6600 mod_chanmode_announce(chanserv, channel, &change);
6601 KickChannelUser(user, channel, chanserv, kick_reason);
6606 /* ChanServ will not modify the limits in join-flooded channels,
6607 or when there are enough slots left below the limit. */
6608 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6609 && !channel->join_flooded
6610 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6612 /* The user count has begun "bumping" into the channel limit,
6613 so set a timer to raise the limit a bit. Any previous
6614 timers are removed so three incoming users within the delay
6615 results in one limit change, not three. */
6617 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6618 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6621 if(channel->join_flooded)
6623 /* don't automatically give ops or voice during a join flood */
6625 else if(cData->lvlOpts[lvlGiveOps] == 0)
6626 modes |= MODE_CHANOP;
6627 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6628 modes |= MODE_VOICE;
6630 greeting = cData->greeting;
6631 if(user->handle_info)
6633 handle = user->handle_info;
6635 if(IsHelper(user) && !IsHelping(user))
6638 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6640 if(channel == chanserv_conf.support_channels.list[ii])
6642 HANDLE_SET_FLAG(user->handle_info, HELPING);
6648 uData = GetTrueChannelAccess(cData, handle);
6649 if(uData && !IsUserSuspended(uData))
6651 /* Ops and above were handled by the above case. */
6652 if(IsUserAutoOp(uData))
6654 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6655 modes |= MODE_CHANOP;
6656 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6657 modes |= MODE_VOICE;
6659 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6660 cData->visited = now;
6661 if(cData->user_greeting)
6662 greeting = cData->user_greeting;
6664 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6665 && ((now - uData->seen) >= chanserv_conf.info_delay)
6673 /* If user joining normally (not during burst), apply op or voice,
6674 * and send greeting/userinfo as appropriate.
6676 if(!user->uplink->burst)
6680 if(modes & MODE_CHANOP)
6681 modes &= ~MODE_VOICE;
6682 change.args[0].mode = modes;
6683 change.args[0].u.member = mNode;
6684 mod_chanmode_announce(chanserv, channel, &change);
6687 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6688 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
6689 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6695 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6697 struct mod_chanmode change;
6698 struct userData *channel;
6699 unsigned int ii, jj;
6701 if(!user->handle_info)
6704 mod_chanmode_init(&change);
6706 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6708 struct chanNode *cn;
6709 struct modeNode *mn;
6710 if(IsUserSuspended(channel)
6711 || IsSuspended(channel->channel)
6712 || !(cn = channel->channel->channel))
6715 mn = GetUserMode(cn, user);
6718 if(!IsUserSuspended(channel)
6719 && IsUserAutoInvite(channel)
6720 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6722 && !user->uplink->burst)
6723 irc_invite(chanserv, user, cn);
6727 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
6728 channel->channel->visited = now;
6730 if(IsUserAutoOp(channel))
6732 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6733 change.args[0].mode = MODE_CHANOP;
6734 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6735 change.args[0].mode = MODE_VOICE;
6737 change.args[0].mode = 0;
6738 change.args[0].u.member = mn;
6739 if(change.args[0].mode)
6740 mod_chanmode_announce(chanserv, cn, &change);
6743 channel->seen = now;
6744 channel->present = 1;
6747 for(ii = 0; ii < user->channels.used; ++ii)
6749 struct chanNode *chan = user->channels.list[ii]->channel;
6750 struct banData *ban;
6752 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6753 || !chan->channel_info
6754 || IsSuspended(chan->channel_info))
6756 for(jj = 0; jj < chan->banlist.used; ++jj)
6757 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6759 if(jj < chan->banlist.used)
6761 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6763 char kick_reason[MAXLEN];
6764 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6766 change.args[0].mode = MODE_BAN;
6767 change.args[0].u.hostmask = ban->mask;
6768 mod_chanmode_announce(chanserv, chan, &change);
6769 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6770 KickChannelUser(user, chan, chanserv, kick_reason);
6771 ban->triggered = now;
6776 if(IsSupportHelper(user))
6778 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6780 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6782 HANDLE_SET_FLAG(user->handle_info, HELPING);
6790 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6792 struct chanData *cData;
6793 struct userData *uData;
6795 cData = mn->channel->channel_info;
6796 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6799 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6801 /* Allow for a bit of padding so that the limit doesn't
6802 track the user count exactly, which could get annoying. */
6803 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6805 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6806 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6810 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6812 scan_user_presence(uData, mn->user);
6814 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6815 cData->visited = now;
6818 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6821 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6822 struct chanNode *channel;
6823 struct userNode *exclude;
6824 /* When looking at the channel that is being /part'ed, we
6825 * have to skip over the client that is leaving. For
6826 * other channels, we must not do that.
6828 channel = chanserv_conf.support_channels.list[ii];
6829 exclude = (channel == mn->channel) ? mn->user : NULL;
6830 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6833 if(ii == chanserv_conf.support_channels.used)
6834 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6839 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6841 struct userData *uData;
6843 if(!channel->channel_info || !kicker || IsService(kicker)
6844 || (kicker == victim) || IsSuspended(channel->channel_info)
6845 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6848 if(protect_user(victim, kicker, channel->channel_info))
6850 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6851 KickChannelUser(kicker, channel, chanserv, reason);
6854 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6859 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6861 struct chanData *cData;
6863 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6866 cData = channel->channel_info;
6867 if(bad_topic(channel, user, channel->topic))
6869 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6870 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6871 SetChannelTopic(channel, chanserv, old_topic, 1);
6872 else if(cData->topic)
6873 SetChannelTopic(channel, chanserv, cData->topic, 1);
6876 /* With topicsnarf, grab the topic and save it as the default topic. */
6877 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6880 cData->topic = strdup(channel->topic);
6886 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6888 struct mod_chanmode *bounce = NULL;
6889 unsigned int bnc, ii;
6892 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6895 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6896 && mode_lock_violated(&channel->channel_info->modes, change))
6898 char correct[MAXLEN];
6899 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6900 mod_chanmode_format(&channel->channel_info->modes, correct);
6901 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6903 for(ii = bnc = 0; ii < change->argc; ++ii)
6905 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6907 const struct userNode *victim = change->args[ii].u.member->user;
6908 if(!protect_user(victim, user, channel->channel_info))
6911 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6914 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6915 bounce->args[bnc].u.member = GetUserMode(channel, user);
6916 if(bounce->args[bnc].u.member)
6920 bounce->args[bnc].mode = MODE_CHANOP;
6921 bounce->args[bnc].u.member = change->args[ii].u.member;
6923 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6925 else if(change->args[ii].mode & MODE_CHANOP)
6927 const struct userNode *victim = change->args[ii].u.member->user;
6928 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6931 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6932 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6933 bounce->args[bnc].u.member = change->args[ii].u.member;
6936 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6938 const char *ban = change->args[ii].u.hostmask;
6939 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6942 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6943 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6944 bounce->args[bnc].u.hostmask = strdup(ban);
6946 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6951 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6952 mod_chanmode_announce(chanserv, channel, bounce);
6953 for(ii = 0; ii < change->argc; ++ii)
6954 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6955 free((char*)bounce->args[ii].u.hostmask);
6956 mod_chanmode_free(bounce);
6961 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6963 struct chanNode *channel;
6964 struct banData *bData;
6965 struct mod_chanmode change;
6966 unsigned int ii, jj;
6967 char kick_reason[MAXLEN];
6969 mod_chanmode_init(&change);
6971 change.args[0].mode = MODE_BAN;
6972 for(ii = 0; ii < user->channels.used; ++ii)
6974 channel = user->channels.list[ii]->channel;
6975 /* Need not check for bans if they're opped or voiced. */
6976 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6978 /* Need not check for bans unless channel registration is active. */
6979 if(!channel->channel_info || IsSuspended(channel->channel_info))
6981 /* Look for a matching ban already on the channel. */
6982 for(jj = 0; jj < channel->banlist.used; ++jj)
6983 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6985 /* Need not act if we found one. */
6986 if(jj < channel->banlist.used)
6988 /* Look for a matching ban in this channel. */
6989 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6991 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6993 change.args[0].u.hostmask = bData->mask;
6994 mod_chanmode_announce(chanserv, channel, &change);
6995 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6996 KickChannelUser(user, channel, chanserv, kick_reason);
6997 bData->triggered = now;
6998 break; /* we don't need to check any more bans in the channel */
7003 static void handle_rename(struct handle_info *handle, const char *old_handle)
7005 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7009 dict_remove2(handle_dnrs, old_handle, 1);
7010 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7011 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7016 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7018 struct userNode *h_user;
7020 if(handle->channels)
7022 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7023 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7025 while(handle->channels)
7026 del_channel_user(handle->channels, 1);
7031 handle_server_link(UNUSED_ARG(struct server *server))
7033 struct chanData *cData;
7035 for(cData = channelList; cData; cData = cData->next)
7037 if(!IsSuspended(cData))
7038 cData->may_opchan = 1;
7039 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7040 && !cData->channel->join_flooded
7041 && ((cData->channel->limit - cData->channel->members.used)
7042 < chanserv_conf.adjust_threshold))
7044 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7045 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7051 chanserv_conf_read(void)
7055 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7056 struct mod_chanmode *change;
7057 struct string_list *strlist;
7058 struct chanNode *chan;
7061 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7063 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7066 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7067 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7068 chanserv_conf.support_channels.used = 0;
7069 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7071 for(ii = 0; ii < strlist->used; ++ii)
7073 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7076 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7078 channelList_append(&chanserv_conf.support_channels, chan);
7081 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7084 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7087 chan = AddChannel(str, now, str2, NULL);
7089 channelList_append(&chanserv_conf.support_channels, chan);
7091 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7092 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7093 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7094 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7095 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7096 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7097 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7098 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7099 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7100 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7101 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7102 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7103 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7104 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7105 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7106 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7107 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7108 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7109 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7110 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7111 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7112 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7113 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7114 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7115 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7116 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7117 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7119 NickChange(chanserv, str, 0);
7120 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7121 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7122 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7123 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7124 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7125 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7126 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7127 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7128 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7129 chanserv_conf.max_owned = str ? atoi(str) : 5;
7130 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7131 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7132 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7133 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7134 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7135 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7136 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7139 safestrncpy(mode_line, str, sizeof(mode_line));
7140 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7141 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7142 && (change->argc < 2))
7144 chanserv_conf.default_modes = *change;
7145 mod_chanmode_free(change);
7147 free_string_list(chanserv_conf.set_shows);
7148 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7150 strlist = string_list_copy(strlist);
7153 static const char *list[] = {
7154 /* free form text */
7155 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7156 /* options based on user level */
7157 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7158 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7159 /* multiple choice options */
7160 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7161 /* binary options */
7162 "DynLimit", "NoDelete",
7166 strlist = alloc_string_list(ArrayLength(list)-1);
7167 for(ii=0; list[ii]; ii++)
7168 string_list_append(strlist, strdup(list[ii]));
7170 chanserv_conf.set_shows = strlist;
7171 /* We don't look things up now, in case the list refers to options
7172 * defined by modules initialized after this point. Just mark the
7173 * function list as invalid, so it will be initialized.
7175 set_shows_list.used = 0;
7176 free_string_list(chanserv_conf.eightball);
7177 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7180 strlist = string_list_copy(strlist);
7184 strlist = alloc_string_list(4);
7185 string_list_append(strlist, strdup("Yes."));
7186 string_list_append(strlist, strdup("No."));
7187 string_list_append(strlist, strdup("Maybe so."));
7189 chanserv_conf.eightball = strlist;
7190 free_string_list(chanserv_conf.old_ban_names);
7191 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7193 strlist = string_list_copy(strlist);
7195 strlist = alloc_string_list(2);
7196 chanserv_conf.old_ban_names = strlist;
7197 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7198 off_channel = str ? atoi(str) : 0;
7202 chanserv_note_type_read(const char *key, struct record_data *rd)
7205 struct note_type *ntype;
7208 if(!(obj = GET_RECORD_OBJECT(rd)))
7210 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7213 if(!(ntype = chanserv_create_note_type(key)))
7215 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7219 /* Figure out set access */
7220 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7222 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7223 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7225 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7227 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7228 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7230 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7232 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7236 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7237 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7238 ntype->set_access.min_opserv = 0;
7241 /* Figure out visibility */
7242 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7243 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7244 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7245 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7246 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7247 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7248 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7249 ntype->visible_type = NOTE_VIS_ALL;
7251 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7253 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7254 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7258 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7260 struct handle_info *handle;
7261 struct userData *uData;
7262 char *seen, *inf, *flags;
7263 unsigned long last_seen;
7264 unsigned short access_level;
7266 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7268 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7272 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7273 if(access_level > UL_OWNER)
7275 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7279 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7280 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7281 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7282 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7283 handle = get_handle_info(key);
7286 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7290 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7291 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7295 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7297 struct banData *bData;
7298 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7299 unsigned long set_time, triggered_time, expires_time;
7301 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7303 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7307 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7308 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7309 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7310 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7311 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7312 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7313 if (!reason || !owner)
7316 set_time = set ? strtoul(set, NULL, 0) : now;
7317 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7319 expires_time = strtoul(s_expires, NULL, 0);
7321 expires_time = set_time + atoi(s_duration);
7325 if(!reason || (expires_time && (expires_time < now)))
7328 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7331 static struct suspended *
7332 chanserv_read_suspended(dict_t obj)
7334 struct suspended *suspended = calloc(1, sizeof(*suspended));
7338 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7339 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7340 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7341 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7342 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7343 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7344 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7345 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7346 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7347 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7352 chanserv_channel_read(const char *key, struct record_data *hir)
7354 struct suspended *suspended;
7355 struct mod_chanmode *modes;
7356 struct chanNode *cNode;
7357 struct chanData *cData;
7358 struct dict *channel, *obj;
7359 char *str, *argv[10];
7363 channel = hir->d.object;
7365 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7368 cNode = AddChannel(key, now, NULL, NULL);
7371 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7374 cData = register_channel(cNode, str);
7377 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7381 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7383 enum levelOption lvlOpt;
7384 enum charOption chOpt;
7386 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7387 cData->flags = atoi(str);
7389 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7391 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7393 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7394 else if(levelOptions[lvlOpt].old_flag)
7396 if(cData->flags & levelOptions[lvlOpt].old_flag)
7397 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7399 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7403 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7405 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7407 cData->chOpts[chOpt] = str[0];
7410 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7412 enum levelOption lvlOpt;
7413 enum charOption chOpt;
7416 cData->flags = base64toint(str, 5);
7417 count = strlen(str += 5);
7418 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7421 if(levelOptions[lvlOpt].old_flag)
7423 if(cData->flags & levelOptions[lvlOpt].old_flag)
7424 lvl = levelOptions[lvlOpt].flag_value;
7426 lvl = levelOptions[lvlOpt].default_value;
7428 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7430 case 'c': lvl = UL_COOWNER; break;
7431 case 'm': lvl = UL_MASTER; break;
7432 case 'n': lvl = UL_OWNER+1; break;
7433 case 'o': lvl = UL_OP; break;
7434 case 'p': lvl = UL_PEON; break;
7435 case 'w': lvl = UL_OWNER; break;
7436 default: lvl = 0; break;
7438 cData->lvlOpts[lvlOpt] = lvl;
7440 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7441 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7444 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7446 suspended = chanserv_read_suspended(obj);
7447 cData->suspended = suspended;
7448 suspended->cData = cData;
7449 /* We could use suspended->expires and suspended->revoked to
7450 * set the CHANNEL_SUSPENDED flag, but we don't. */
7452 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7454 suspended = calloc(1, sizeof(*suspended));
7455 suspended->issued = 0;
7456 suspended->revoked = 0;
7457 suspended->suspender = strdup(str);
7458 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7459 suspended->expires = str ? atoi(str) : 0;
7460 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7461 suspended->reason = strdup(str ? str : "No reason");
7462 suspended->previous = NULL;
7463 cData->suspended = suspended;
7464 suspended->cData = cData;
7468 cData->flags &= ~CHANNEL_SUSPENDED;
7469 suspended = NULL; /* to squelch a warning */
7472 if(IsSuspended(cData)) {
7473 if(suspended->expires > now)
7474 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7475 else if(suspended->expires)
7476 cData->flags &= ~CHANNEL_SUSPENDED;
7479 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7480 struct mod_chanmode change;
7481 mod_chanmode_init(&change);
7483 change.args[0].mode = MODE_CHANOP;
7484 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7485 mod_chanmode_announce(chanserv, cNode, &change);
7488 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7489 cData->registered = str ? strtoul(str, NULL, 0) : now;
7490 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7491 cData->visited = str ? strtoul(str, NULL, 0) : now;
7492 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7493 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7494 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7495 cData->max = str ? atoi(str) : 0;
7496 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7497 cData->greeting = str ? strdup(str) : NULL;
7498 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7499 cData->user_greeting = str ? strdup(str) : NULL;
7500 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7501 cData->topic_mask = str ? strdup(str) : NULL;
7502 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7503 cData->topic = str ? strdup(str) : NULL;
7505 if(!IsSuspended(cData)
7506 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7507 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7508 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
7509 cData->modes = *modes;
7511 cData->modes.modes_set |= MODE_REGISTERED;
7512 if(cData->modes.argc > 1)
7513 cData->modes.argc = 1;
7514 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7515 mod_chanmode_free(modes);
7518 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7519 for(it = dict_first(obj); it; it = iter_next(it))
7520 user_read_helper(iter_key(it), iter_data(it), cData);
7522 if(!cData->users && !IsProtected(cData))
7524 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7525 unregister_channel(cData, "has empty user list.");
7529 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7530 for(it = dict_first(obj); it; it = iter_next(it))
7531 ban_read_helper(iter_key(it), iter_data(it), cData);
7533 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7534 for(it = dict_first(obj); it; it = iter_next(it))
7536 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7537 struct record_data *rd = iter_data(it);
7538 const char *note, *setter;
7540 if(rd->type != RECDB_OBJECT)
7542 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7546 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7548 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7550 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7554 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7555 if(!setter) setter = "<unknown>";
7556 chanserv_add_channel_note(cData, ntype, setter, note);
7564 chanserv_dnr_read(const char *key, struct record_data *hir)
7566 const char *setter, *reason, *str;
7567 struct do_not_register *dnr;
7568 unsigned long expiry;
7570 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7573 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7576 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7579 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7582 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7583 expiry = str ? strtoul(str, NULL, 0) : 0;
7584 if(expiry && expiry <= now)
7586 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7589 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7591 dnr->set = atoi(str);
7597 chanserv_saxdb_read(struct dict *database)
7599 struct dict *section;
7602 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7603 for(it = dict_first(section); it; it = iter_next(it))
7604 chanserv_note_type_read(iter_key(it), iter_data(it));
7606 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7607 for(it = dict_first(section); it; it = iter_next(it))
7608 chanserv_channel_read(iter_key(it), iter_data(it));
7610 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7611 for(it = dict_first(section); it; it = iter_next(it))
7612 chanserv_dnr_read(iter_key(it), iter_data(it));
7618 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7620 int high_present = 0;
7621 saxdb_start_record(ctx, KEY_USERS, 1);
7622 for(; uData; uData = uData->next)
7624 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
7626 saxdb_start_record(ctx, uData->handle->handle, 0);
7627 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7628 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7630 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7632 saxdb_write_string(ctx, KEY_INFO, uData->info);
7633 saxdb_end_record(ctx);
7635 saxdb_end_record(ctx);
7636 return high_present;
7640 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7644 saxdb_start_record(ctx, KEY_BANS, 1);
7645 for(; bData; bData = bData->next)
7647 saxdb_start_record(ctx, bData->mask, 0);
7648 saxdb_write_int(ctx, KEY_SET, bData->set);
7649 if(bData->triggered)
7650 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7652 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7654 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7656 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7657 saxdb_end_record(ctx);
7659 saxdb_end_record(ctx);
7663 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7665 saxdb_start_record(ctx, name, 0);
7666 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7667 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7669 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7671 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7673 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7675 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7676 saxdb_end_record(ctx);
7680 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7684 enum levelOption lvlOpt;
7685 enum charOption chOpt;
7687 saxdb_start_record(ctx, channel->channel->name, 1);
7689 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7690 saxdb_write_int(ctx, KEY_MAX, channel->max);
7692 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7693 if(channel->registrar)
7694 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7695 if(channel->greeting)
7696 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7697 if(channel->user_greeting)
7698 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7699 if(channel->topic_mask)
7700 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7701 if(channel->suspended)
7702 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7704 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7705 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7706 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7707 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7708 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7710 buf[0] = channel->chOpts[chOpt];
7712 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7714 saxdb_end_record(ctx);
7716 if(channel->modes.modes_set || channel->modes.modes_clear)
7718 mod_chanmode_format(&channel->modes, buf);
7719 saxdb_write_string(ctx, KEY_MODES, buf);
7722 high_present = chanserv_write_users(ctx, channel->users);
7723 chanserv_write_bans(ctx, channel->bans);
7725 if(dict_size(channel->notes))
7729 saxdb_start_record(ctx, KEY_NOTES, 1);
7730 for(it = dict_first(channel->notes); it; it = iter_next(it))
7732 struct note *note = iter_data(it);
7733 saxdb_start_record(ctx, iter_key(it), 0);
7734 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7735 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7736 saxdb_end_record(ctx);
7738 saxdb_end_record(ctx);
7741 if(channel->ownerTransfer)
7742 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7743 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7744 saxdb_end_record(ctx);
7748 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7752 saxdb_start_record(ctx, ntype->name, 0);
7753 switch(ntype->set_access_type)
7755 case NOTE_SET_CHANNEL_ACCESS:
7756 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7758 case NOTE_SET_CHANNEL_SETTER:
7759 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7761 case NOTE_SET_PRIVILEGED: default:
7762 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7765 switch(ntype->visible_type)
7767 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7768 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7769 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7771 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7772 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7773 saxdb_end_record(ctx);
7777 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7779 struct do_not_register *dnr;
7780 dict_iterator_t it, next;
7782 for(it = dict_first(dnrs); it; it = next)
7784 next = iter_next(it);
7785 dnr = iter_data(it);
7786 if(dnr->expires && dnr->expires <= now)
7788 dict_remove(dnrs, iter_key(it));
7791 saxdb_start_record(ctx, dnr->chan_name, 0);
7793 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7795 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7796 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7797 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7798 saxdb_end_record(ctx);
7803 chanserv_saxdb_write(struct saxdb_context *ctx)
7806 struct chanData *channel;
7809 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7810 for(it = dict_first(note_types); it; it = iter_next(it))
7811 chanserv_write_note_type(ctx, iter_data(it));
7812 saxdb_end_record(ctx);
7815 saxdb_start_record(ctx, KEY_DNR, 1);
7816 write_dnrs_helper(ctx, handle_dnrs);
7817 write_dnrs_helper(ctx, plain_dnrs);
7818 write_dnrs_helper(ctx, mask_dnrs);
7819 saxdb_end_record(ctx);
7822 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7823 for(channel = channelList; channel; channel = channel->next)
7824 chanserv_write_channel(ctx, channel);
7825 saxdb_end_record(ctx);
7831 chanserv_db_cleanup(void) {
7833 unreg_part_func(handle_part);
7835 unregister_channel(channelList, "terminating.");
7836 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7837 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7838 free(chanserv_conf.support_channels.list);
7839 dict_delete(handle_dnrs);
7840 dict_delete(plain_dnrs);
7841 dict_delete(mask_dnrs);
7842 dict_delete(note_types);
7843 free_string_list(chanserv_conf.eightball);
7844 free_string_list(chanserv_conf.old_ban_names);
7845 free_string_list(chanserv_conf.set_shows);
7846 free(set_shows_list.list);
7847 free(uset_shows_list.list);
7850 struct userData *helper = helperList;
7851 helperList = helperList->next;
7856 #if defined(GCC_VARMACROS)
7857 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7858 #elif defined(C99_VARMACROS)
7859 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7861 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7862 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7865 init_chanserv(const char *nick)
7867 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7868 conf_register_reload(chanserv_conf_read);
7872 reg_server_link_func(handle_server_link);
7873 reg_new_channel_func(handle_new_channel);
7874 reg_join_func(handle_join);
7875 reg_part_func(handle_part);
7876 reg_kick_func(handle_kick);
7877 reg_topic_func(handle_topic);
7878 reg_mode_change_func(handle_mode);
7879 reg_nick_change_func(handle_nick_change);
7880 reg_auth_func(handle_auth);
7883 reg_handle_rename_func(handle_rename);
7884 reg_unreg_func(handle_unreg);
7886 handle_dnrs = dict_new();
7887 dict_set_free_data(handle_dnrs, free);
7888 plain_dnrs = dict_new();
7889 dict_set_free_data(plain_dnrs, free);
7890 mask_dnrs = dict_new();
7891 dict_set_free_data(mask_dnrs, free);
7893 reg_svccmd_unbind_func(handle_svccmd_unbind);
7894 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7895 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7896 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7897 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7898 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7899 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7900 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7901 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7902 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7903 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7904 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7905 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7906 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7908 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7909 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7911 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7912 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7913 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7914 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7915 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7917 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7918 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7919 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7920 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7921 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7923 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7924 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7925 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7926 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7928 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7929 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7930 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7931 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7932 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7933 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7934 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7935 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7937 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7938 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7939 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7940 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7941 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7942 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7943 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7944 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7945 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7946 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7947 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7948 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7949 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7950 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7952 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7953 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7954 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7955 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7956 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7958 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7959 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7961 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7962 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7963 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7964 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7965 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7966 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7967 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7968 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7969 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7970 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7971 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7973 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7974 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7976 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7977 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7978 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7979 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7981 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7982 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7983 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7984 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7985 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7987 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7988 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7989 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7990 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7991 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7992 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7994 /* Channel options */
7995 DEFINE_CHANNEL_OPTION(defaulttopic);
7996 DEFINE_CHANNEL_OPTION(topicmask);
7997 DEFINE_CHANNEL_OPTION(greeting);
7998 DEFINE_CHANNEL_OPTION(usergreeting);
7999 DEFINE_CHANNEL_OPTION(modes);
8000 DEFINE_CHANNEL_OPTION(enfops);
8001 DEFINE_CHANNEL_OPTION(giveops);
8002 DEFINE_CHANNEL_OPTION(protect);
8003 DEFINE_CHANNEL_OPTION(enfmodes);
8004 DEFINE_CHANNEL_OPTION(enftopic);
8005 DEFINE_CHANNEL_OPTION(pubcmd);
8006 DEFINE_CHANNEL_OPTION(givevoice);
8007 DEFINE_CHANNEL_OPTION(userinfo);
8008 DEFINE_CHANNEL_OPTION(dynlimit);
8009 DEFINE_CHANNEL_OPTION(topicsnarf);
8010 DEFINE_CHANNEL_OPTION(nodelete);
8011 DEFINE_CHANNEL_OPTION(toys);
8012 DEFINE_CHANNEL_OPTION(setters);
8013 DEFINE_CHANNEL_OPTION(topicrefresh);
8014 DEFINE_CHANNEL_OPTION(ctcpusers);
8015 DEFINE_CHANNEL_OPTION(ctcpreaction);
8016 DEFINE_CHANNEL_OPTION(inviteme);
8017 DEFINE_CHANNEL_OPTION(unreviewed);
8018 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8019 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8021 DEFINE_CHANNEL_OPTION(offchannel);
8022 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8024 /* Alias set topic to set defaulttopic for compatibility. */
8025 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8028 DEFINE_USER_OPTION(noautoop);
8029 DEFINE_USER_OPTION(autoinvite);
8030 DEFINE_USER_OPTION(info);
8032 /* Alias uset autovoice to uset autoop. */
8033 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8035 note_types = dict_new();
8036 dict_set_free_data(note_types, chanserv_deref_note_type);
8039 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8040 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8041 service_register(chanserv)->trigger = '!';
8042 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8044 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8046 if(chanserv_conf.channel_expire_frequency)
8047 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8049 if(chanserv_conf.dnr_expire_frequency)
8050 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8052 if(chanserv_conf.refresh_period)
8054 unsigned long next_refresh;
8055 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8056 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8059 reg_exit_func(chanserv_db_cleanup);
8060 message_register_table(msgtab);