1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 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() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "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." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "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." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
203 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
204 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
205 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
206 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
207 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
208 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
209 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
210 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
211 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
212 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
213 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
214 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
215 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
216 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
217 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
218 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
220 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
222 /* Channel management */
223 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
224 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
225 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
227 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
228 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
229 { "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" },
230 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
231 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
232 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
233 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
235 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
236 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
237 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
238 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
239 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
240 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
241 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
242 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
243 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
244 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
245 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
246 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
247 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
248 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
249 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
250 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
251 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
252 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
253 { "CSMSG_SET_MODES", "$bModes $b %s" },
254 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
255 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
256 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
257 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
258 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
259 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
260 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
261 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
262 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
263 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
264 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
265 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
266 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
267 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
268 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
269 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
270 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
271 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
272 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
273 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
274 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
275 { "CSMSG_USET_INFO", "$bInfo $b %s" },
277 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
278 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
279 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
280 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
281 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
282 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
283 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
284 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
287 { "CSMSG_PROTECT_NONE", "No users will be protected." },
288 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
289 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
290 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
291 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
292 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
293 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
294 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
295 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
296 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
301 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
302 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
303 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
304 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
305 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
306 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
307 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
308 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
310 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
311 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
312 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
314 /* Channel userlist */
315 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
316 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
317 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
318 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
320 /* Channel note list */
321 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
322 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
323 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
324 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
325 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
326 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
327 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
328 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
329 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
330 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
331 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
332 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
333 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
334 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
335 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
337 /* Channel [un]suspension */
338 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
339 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
340 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
341 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
342 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
343 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
344 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
346 /* Access information */
347 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
348 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
349 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
350 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
351 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
352 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
353 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
354 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
355 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
356 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
357 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
359 /* Seen information */
360 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
361 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
362 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
363 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
365 /* Names information */
366 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
367 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
369 /* Channel information */
370 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
371 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
372 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
373 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
374 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
375 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
376 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
377 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
378 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
379 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
380 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
381 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
390 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
392 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
393 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
394 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
395 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
396 { "CSMSG_PEEK_OPS", "$bOps:$b" },
397 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
399 /* Network information */
400 { "CSMSG_NETWORK_INFO", "Network Information:" },
401 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
402 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
403 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
404 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
405 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
406 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
407 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
408 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
411 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
412 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
413 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
415 /* Channel searches */
416 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
417 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
418 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
419 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
421 /* Channel configuration */
422 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
423 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
424 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
427 { "CSMSG_USER_OPTIONS", "User Options:" },
428 { "CSMSG_USER_PROTECTED", "That user is protected." },
431 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
432 { "CSMSG_PING_RESPONSE", "Pong!" },
433 { "CSMSG_WUT_RESPONSE", "wut" },
434 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
435 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
436 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
437 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
438 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
439 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
440 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
443 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
447 /* eject_user and unban_user flags */
448 #define ACTION_KICK 0x0001
449 #define ACTION_BAN 0x0002
450 #define ACTION_ADD_BAN 0x0004
451 #define ACTION_ADD_TIMED_BAN 0x0008
452 #define ACTION_UNBAN 0x0010
453 #define ACTION_DEL_BAN 0x0020
455 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
456 #define MODELEN 40 + KEYLEN
460 #define CSFUNC_ARGS user, channel, argc, argv, cmd
462 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
463 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
464 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
465 reply("MSG_MISSING_PARAMS", argv[0]); \
469 DECLARE_LIST(dnrList, struct do_not_register *);
470 DEFINE_LIST(dnrList, struct do_not_register *);
472 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
474 struct userNode *chanserv;
477 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
478 static struct log_type *CS_LOG;
482 struct channelList support_channels;
483 struct mod_chanmode default_modes;
485 unsigned long db_backup_frequency;
486 unsigned long channel_expire_frequency;
489 unsigned int adjust_delay;
490 long channel_expire_delay;
491 unsigned int nodelete_level;
493 unsigned int adjust_threshold;
494 int join_flood_threshold;
496 unsigned int greeting_length;
497 unsigned int refresh_period;
498 unsigned int giveownership_period;
500 unsigned int max_owned;
501 unsigned int max_chan_users;
502 unsigned int max_chan_bans;
503 unsigned int max_userinfo_length;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->ownerTransfer = now;
1084 channel->flags = CHANNEL_DEFAULT_FLAGS;
1085 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1086 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1087 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1088 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1090 channel->prev = NULL;
1091 channel->next = channelList;
1094 channelList->prev = channel;
1095 channelList = channel;
1096 registered_channels++;
1098 channel->channel = cNode;
1100 cNode->channel_info = channel;
1105 static struct userData*
1106 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1108 struct userData *ud;
1110 if(access > UL_OWNER)
1113 ud = calloc(1, sizeof(*ud));
1114 ud->channel = channel;
1115 ud->handle = handle;
1117 ud->access = access;
1118 ud->info = info ? strdup(info) : NULL;
1121 ud->next = channel->users;
1123 channel->users->prev = ud;
1124 channel->users = ud;
1126 channel->userCount++;
1130 ud->u_next = ud->handle->channels;
1132 ud->u_next->u_prev = ud;
1133 ud->handle->channels = ud;
1138 static void unregister_channel(struct chanData *channel, const char *reason);
1141 del_channel_user(struct userData *user, int do_gc)
1143 struct chanData *channel = user->channel;
1145 channel->userCount--;
1149 user->prev->next = user->next;
1151 channel->users = user->next;
1153 user->next->prev = user->prev;
1156 user->u_prev->u_next = user->u_next;
1158 user->handle->channels = user->u_next;
1160 user->u_next->u_prev = user->u_prev;
1164 if(do_gc && !channel->users && !IsProtected(channel))
1165 unregister_channel(channel, "lost all users.");
1168 static void expire_ban(void *data);
1170 static struct banData*
1171 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1174 unsigned int ii, l1, l2;
1179 bd = malloc(sizeof(struct banData));
1181 bd->channel = channel;
1183 bd->triggered = triggered;
1184 bd->expires = expires;
1186 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1188 extern const char *hidden_host_suffix;
1189 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1193 l2 = strlen(old_name);
1196 if(irccasecmp(mask + l1 - l2, old_name))
1198 new_mask = alloca(MAXLEN);
1199 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1202 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1204 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1205 bd->reason = strdup(reason);
1208 timeq_add(expires, expire_ban, bd);
1211 bd->next = channel->bans;
1213 channel->bans->prev = bd;
1215 channel->banCount++;
1222 del_channel_ban(struct banData *ban)
1224 ban->channel->banCount--;
1228 ban->prev->next = ban->next;
1230 ban->channel->bans = ban->next;
1233 ban->next->prev = ban->prev;
1236 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1245 expire_ban(void *data)
1247 struct banData *bd = data;
1248 if(!IsSuspended(bd->channel))
1250 struct banList bans;
1251 struct mod_chanmode change;
1253 bans = bd->channel->channel->banlist;
1254 mod_chanmode_init(&change);
1255 for(ii=0; ii<bans.used; ii++)
1257 if(!strcmp(bans.list[ii]->ban, bd->mask))
1260 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1261 change.args[0].u.hostmask = bd->mask;
1262 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1268 del_channel_ban(bd);
1271 static void chanserv_expire_suspension(void *data);
1274 unregister_channel(struct chanData *channel, const char *reason)
1276 struct mod_chanmode change;
1277 char msgbuf[MAXLEN];
1279 /* After channel unregistration, the following must be cleaned
1281 - Channel information.
1284 - Channel suspension data.
1285 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1291 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 mod_chanmode_init(&change);
1296 change.modes_clear |= MODE_REGISTERED;
1297 mod_chanmode_announce(chanserv, channel->channel, &change);
1300 while(channel->users)
1301 del_channel_user(channel->users, 0);
1303 while(channel->bans)
1304 del_channel_ban(channel->bans);
1306 free(channel->topic);
1307 free(channel->registrar);
1308 free(channel->greeting);
1309 free(channel->user_greeting);
1310 free(channel->topic_mask);
1313 channel->prev->next = channel->next;
1315 channelList = channel->next;
1318 channel->next->prev = channel->prev;
1320 if(channel->suspended)
1322 struct chanNode *cNode = channel->channel;
1323 struct suspended *suspended, *next_suspended;
1325 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1327 next_suspended = suspended->previous;
1328 free(suspended->suspender);
1329 free(suspended->reason);
1330 if(suspended->expires)
1331 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1336 cNode->channel_info = NULL;
1338 channel->channel->channel_info = NULL;
1340 dict_delete(channel->notes);
1341 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1342 if(!IsSuspended(channel))
1343 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1344 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1345 UnlockChannel(channel->channel);
1347 registered_channels--;
1351 expire_channels(UNUSED_ARG(void *data))
1353 struct chanData *channel, *next;
1354 struct userData *user;
1355 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1357 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1358 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1360 for(channel = channelList; channel; channel = next)
1362 next = channel->next;
1364 /* See if the channel can be expired. */
1365 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1366 || IsProtected(channel))
1369 /* Make sure there are no high-ranking users still in the channel. */
1370 for(user=channel->users; user; user=user->next)
1371 if(user->present && (user->access >= UL_PRESENT))
1376 /* Unregister the channel */
1377 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1378 unregister_channel(channel, "registration expired.");
1381 if(chanserv_conf.channel_expire_frequency)
1382 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1386 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1388 char protect = channel->chOpts[chProtect];
1389 struct userData *cs_victim, *cs_aggressor;
1391 /* Don't protect if no one is to be protected, someone is attacking
1392 himself, or if the aggressor is an IRC Operator. */
1393 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1396 /* Don't protect if the victim isn't authenticated (because they
1397 can't be a channel user), unless we are to protect non-users
1399 cs_victim = GetChannelAccess(channel, victim->handle_info);
1400 if(protect != 'a' && !cs_victim)
1403 /* Protect if the aggressor isn't a user because at this point,
1404 the aggressor can only be less than or equal to the victim. */
1405 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1409 /* If the aggressor was a user, then the victim can't be helped. */
1416 if(cs_victim->access > cs_aggressor->access)
1421 if(cs_victim->access >= cs_aggressor->access)
1430 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1432 struct chanData *cData = channel->channel_info;
1433 struct userData *cs_victim;
1435 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1436 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1437 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1439 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1447 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1449 if(IsService(victim))
1451 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1455 if(protect_user(victim, user, channel->channel_info))
1457 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1464 static struct do_not_register *
1465 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1467 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1468 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1469 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1470 strcpy(dnr->reason, reason);
1472 if(dnr->chan_name[0] == '*')
1473 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1474 else if(strpbrk(dnr->chan_name, "*?"))
1475 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1477 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1481 static struct dnrList
1482 chanserv_find_dnrs(const char *chan_name, const char *handle)
1484 struct dnrList list;
1486 struct do_not_register *dnr;
1488 dnrList_init(&list);
1489 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1490 dnrList_append(&list, dnr);
1491 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1492 dnrList_append(&list, dnr);
1494 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1495 if(match_ircglob(chan_name, iter_key(it)))
1496 dnrList_append(&list, iter_data(it));
1501 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1503 struct dnrList list;
1504 struct do_not_register *dnr;
1506 char buf[INTERVALLEN];
1508 list = chanserv_find_dnrs(chan_name, handle);
1509 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1511 dnr = list.list[ii];
1514 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1515 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1518 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1521 reply("CSMSG_MORE_DNRS", list.used - ii);
1526 struct do_not_register *
1527 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1529 struct do_not_register *dnr;
1532 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1536 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1538 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1539 if(match_ircglob(chan_name, iter_key(it)))
1540 return iter_data(it);
1545 static CHANSERV_FUNC(cmd_noregister)
1548 struct do_not_register *dnr;
1549 char buf[INTERVALLEN];
1550 unsigned int matches;
1556 reply("CSMSG_DNR_SEARCH_RESULTS");
1558 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1560 dnr = iter_data(it);
1562 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1564 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1567 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1569 dnr = iter_data(it);
1571 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1573 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1576 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1578 dnr = iter_data(it);
1580 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1582 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1587 reply("MSG_MATCH_COUNT", matches);
1589 reply("MSG_NO_MATCHES");
1595 if(!IsChannelName(target) && (*target != '*'))
1597 reply("CSMSG_NOT_DNR", target);
1603 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1604 if((*target == '*') && !get_handle_info(target + 1))
1606 reply("MSG_HANDLE_UNKNOWN", target + 1);
1609 chanserv_add_dnr(target, user->handle_info->handle, reason);
1610 reply("CSMSG_NOREGISTER_CHANNEL", target);
1614 reply("CSMSG_DNR_SEARCH_RESULTS");
1616 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1618 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1620 reply("MSG_NO_MATCHES");
1624 static CHANSERV_FUNC(cmd_allowregister)
1626 const char *chan_name = argv[1];
1628 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1630 dict_remove(handle_dnrs, chan_name+1);
1631 reply("CSMSG_DNR_REMOVED", chan_name);
1633 else if(dict_find(plain_dnrs, chan_name, NULL))
1635 dict_remove(plain_dnrs, chan_name);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(mask_dnrs, chan_name, NULL))
1640 dict_remove(mask_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1645 reply("CSMSG_NO_SUCH_DNR", chan_name);
1652 chanserv_get_owned_count(struct handle_info *hi)
1654 struct userData *cList;
1657 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1658 if(cList->access == UL_OWNER)
1663 static CHANSERV_FUNC(cmd_register)
1665 struct handle_info *handle;
1666 struct chanData *cData;
1667 struct modeNode *mn;
1668 char reason[MAXLEN];
1670 unsigned int new_channel, force=0;
1671 struct do_not_register *dnr;
1675 if(channel->channel_info)
1677 reply("CSMSG_ALREADY_REGGED", channel->name);
1681 if(channel->bad_channel)
1683 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1688 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1690 reply("CSMSG_MUST_BE_OPPED", channel->name);
1695 chan_name = channel->name;
1699 if((argc < 2) || !IsChannelName(argv[1]))
1701 reply("MSG_NOT_CHANNEL_NAME");
1705 if(opserv_bad_channel(argv[1]))
1707 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1712 chan_name = argv[1];
1715 if(argc >= (new_channel+2))
1717 if(!IsHelping(user))
1719 reply("CSMSG_PROXY_FORBIDDEN");
1723 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1725 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1726 dnr = chanserv_is_dnr(chan_name, handle);
1730 handle = user->handle_info;
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 if(!IsHelping(user))
1736 reply("CSMSG_DNR_CHANNEL", chan_name);
1738 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1742 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1744 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1749 channel = AddChannel(argv[1], now, NULL, NULL);
1751 cData = register_channel(channel, user->handle_info->handle);
1752 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1753 cData->modes = chanserv_conf.default_modes;
1755 cData->modes.modes_set |= MODE_REGISTERED;
1756 if (IsOffChannel(cData))
1758 mod_chanmode_announce(chanserv, channel, &cData->modes);
1762 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1763 change->args[change->argc].mode = MODE_CHANOP;
1764 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1766 mod_chanmode_announce(chanserv, channel, change);
1767 mod_chanmode_free(change);
1770 /* Initialize the channel's max user record. */
1771 cData->max = channel->members.used;
1773 if(handle != user->handle_info)
1774 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1776 reply("CSMSG_REG_SUCCESS", channel->name);
1778 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1779 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1784 make_confirmation_string(struct userData *uData)
1786 static char strbuf[16];
1791 for(src = uData->handle->handle; *src; )
1792 accum = accum * 31 + toupper(*src++);
1794 for(src = uData->channel->channel->name; *src; )
1795 accum = accum * 31 + toupper(*src++);
1796 sprintf(strbuf, "%08x", accum);
1800 static CHANSERV_FUNC(cmd_unregister)
1803 char reason[MAXLEN];
1804 struct chanData *cData;
1805 struct userData *uData;
1807 cData = channel->channel_info;
1810 reply("CSMSG_NOT_REGISTERED", channel->name);
1814 uData = GetChannelUser(cData, user->handle_info);
1815 if(!uData || (uData->access < UL_OWNER))
1817 reply("CSMSG_NO_ACCESS");
1821 if(IsProtected(cData))
1823 reply("CSMSG_UNREG_NODELETE", channel->name);
1827 if(!IsHelping(user))
1829 const char *confirm_string;
1830 if(IsSuspended(cData))
1832 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1835 confirm_string = make_confirmation_string(uData);
1836 if((argc < 2) || strcmp(argv[1], confirm_string))
1838 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1843 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1844 name = strdup(channel->name);
1845 unregister_channel(cData, reason);
1846 reply("CSMSG_UNREG_SUCCESS", name);
1851 static CHANSERV_FUNC(cmd_move)
1853 struct mod_chanmode change;
1854 struct chanNode *target;
1855 struct modeNode *mn;
1856 struct userData *uData;
1857 char reason[MAXLEN];
1858 struct do_not_register *dnr;
1862 if(IsProtected(channel->channel_info))
1864 reply("CSMSG_MOVE_NODELETE", channel->name);
1868 if(!IsChannelName(argv[1]))
1870 reply("MSG_NOT_CHANNEL_NAME");
1874 if(opserv_bad_channel(argv[1]))
1876 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1880 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1882 for(uData = channel->channel_info->users; uData; uData = uData->next)
1884 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1886 if(!IsHelping(user))
1887 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1889 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1895 mod_chanmode_init(&change);
1896 if(!(target = GetChannel(argv[1])))
1898 target = AddChannel(argv[1], now, NULL, NULL);
1899 if(!IsSuspended(channel->channel_info))
1900 AddChannelUser(chanserv, target);
1902 else if(target->channel_info)
1904 reply("CSMSG_ALREADY_REGGED", target->name);
1907 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1908 && !IsHelping(user))
1910 reply("CSMSG_MUST_BE_OPPED", target->name);
1913 else if(!IsSuspended(channel->channel_info))
1916 change.args[0].mode = MODE_CHANOP;
1917 change.args[0].u.member = AddChannelUser(chanserv, target);
1918 mod_chanmode_announce(chanserv, target, &change);
1923 /* Clear MODE_REGISTERED from old channel, add it to new. */
1925 change.modes_clear = MODE_REGISTERED;
1926 mod_chanmode_announce(chanserv, channel, &change);
1927 change.modes_clear = 0;
1928 change.modes_set = MODE_REGISTERED;
1929 mod_chanmode_announce(chanserv, target, &change);
1932 /* Move the channel_info to the target channel; it
1933 shouldn't be necessary to clear timeq callbacks
1934 for the old channel. */
1935 target->channel_info = channel->channel_info;
1936 target->channel_info->channel = target;
1937 channel->channel_info = NULL;
1939 reply("CSMSG_MOVE_SUCCESS", target->name);
1941 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1942 if(!IsSuspended(target->channel_info))
1944 char reason2[MAXLEN];
1945 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1946 DelChannelUser(chanserv, channel, reason2, 0);
1948 UnlockChannel(channel);
1949 LockChannel(target);
1950 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1955 merge_users(struct chanData *source, struct chanData *target)
1957 struct userData *suData, *tuData, *next;
1963 /* Insert the source's users into the scratch area. */
1964 for(suData = source->users; suData; suData = suData->next)
1965 dict_insert(merge, suData->handle->handle, suData);
1967 /* Iterate through the target's users, looking for
1968 users common to both channels. The lower access is
1969 removed from either the scratch area or target user
1971 for(tuData = target->users; tuData; tuData = next)
1973 struct userData *choice;
1975 next = tuData->next;
1977 /* If a source user exists with the same handle as a target
1978 channel's user, resolve the conflict by removing one. */
1979 suData = dict_find(merge, tuData->handle->handle, NULL);
1983 /* Pick the data we want to keep. */
1984 /* If the access is the same, use the later seen time. */
1985 if(suData->access == tuData->access)
1986 choice = (suData->seen > tuData->seen) ? suData : tuData;
1987 else /* Otherwise, keep the higher access level. */
1988 choice = (suData->access > tuData->access) ? suData : tuData;
1990 /* Remove the user that wasn't picked. */
1991 if(choice == tuData)
1993 dict_remove(merge, suData->handle->handle);
1994 del_channel_user(suData, 0);
1997 del_channel_user(tuData, 0);
2000 /* Move the remaining users to the target channel. */
2001 for(it = dict_first(merge); it; it = iter_next(it))
2003 suData = iter_data(it);
2005 /* Insert the user into the target channel's linked list. */
2006 suData->prev = NULL;
2007 suData->next = target->users;
2008 suData->channel = target;
2011 target->users->prev = suData;
2012 target->users = suData;
2014 /* Update the user counts for the target channel; the
2015 source counts are left alone. */
2016 target->userCount++;
2019 /* Possible to assert (source->users == NULL) here. */
2020 source->users = NULL;
2025 merge_bans(struct chanData *source, struct chanData *target)
2027 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2029 /* Hold on to the original head of the target ban list
2030 to avoid comparing source bans with source bans. */
2031 tFront = target->bans;
2033 /* Perform a totally expensive O(n*m) merge, ick. */
2034 for(sbData = source->bans; sbData; sbData = sNext)
2036 /* Flag to track whether the ban's been moved
2037 to the destination yet. */
2040 /* Possible to assert (sbData->prev == NULL) here. */
2041 sNext = sbData->next;
2043 for(tbData = tFront; tbData; tbData = tNext)
2045 tNext = tbData->next;
2047 /* Perform two comparisons between each source
2048 and target ban, conflicts are resolved by
2049 keeping the broader ban and copying the later
2050 expiration and triggered time. */
2051 if(match_ircglobs(tbData->mask, sbData->mask))
2053 /* There is a broader ban in the target channel that
2054 overrides one in the source channel; remove the
2055 source ban and break. */
2056 if(sbData->expires > tbData->expires)
2057 tbData->expires = sbData->expires;
2058 if(sbData->triggered > tbData->triggered)
2059 tbData->triggered = sbData->triggered;
2060 del_channel_ban(sbData);
2063 else if(match_ircglobs(sbData->mask, tbData->mask))
2065 /* There is a broader ban in the source channel that
2066 overrides one in the target channel; remove the
2067 target ban, fall through and move the source over. */
2068 if(tbData->expires > sbData->expires)
2069 sbData->expires = tbData->expires;
2070 if(tbData->triggered > sbData->triggered)
2071 sbData->triggered = tbData->triggered;
2072 if(tbData == tFront)
2074 del_channel_ban(tbData);
2077 /* Source bans can override multiple target bans, so
2078 we allow a source to run through this loop multiple
2079 times, but we can only move it once. */
2084 /* Remove the source ban from the source ban list. */
2086 sbData->next->prev = sbData->prev;
2088 /* Modify the source ban's associated channel. */
2089 sbData->channel = target;
2091 /* Insert the ban into the target channel's linked list. */
2092 sbData->prev = NULL;
2093 sbData->next = target->bans;
2096 target->bans->prev = sbData;
2097 target->bans = sbData;
2099 /* Update the user counts for the target channel. */
2104 /* Possible to assert (source->bans == NULL) here. */
2105 source->bans = NULL;
2109 merge_data(struct chanData *source, struct chanData *target)
2111 /* Use more recent visited and owner-transfer time; use older
2112 * registered time. Bitwise or may_opchan. Use higher max.
2113 * Do not touch last_refresh, ban count or user counts.
2115 if(source->visited > target->visited)
2116 target->visited = source->visited;
2117 if(source->registered < target->registered)
2118 target->registered = source->registered;
2119 if(source->ownerTransfer > target->ownerTransfer)
2120 target->ownerTransfer = source->ownerTransfer;
2121 if(source->may_opchan)
2122 target->may_opchan = 1;
2123 if(source->max > target->max)
2124 target->max = source->max;
2128 merge_channel(struct chanData *source, struct chanData *target)
2130 merge_users(source, target);
2131 merge_bans(source, target);
2132 merge_data(source, target);
2135 static CHANSERV_FUNC(cmd_merge)
2137 struct userData *target_user;
2138 struct chanNode *target;
2139 char reason[MAXLEN];
2143 /* Make sure the target channel exists and is registered to the user
2144 performing the command. */
2145 if(!(target = GetChannel(argv[1])))
2147 reply("MSG_INVALID_CHANNEL");
2151 if(!target->channel_info)
2153 reply("CSMSG_NOT_REGISTERED", target->name);
2157 if(IsProtected(channel->channel_info))
2159 reply("CSMSG_MERGE_NODELETE");
2163 if(IsSuspended(target->channel_info))
2165 reply("CSMSG_MERGE_SUSPENDED");
2169 if(channel == target)
2171 reply("CSMSG_MERGE_SELF");
2175 target_user = GetChannelUser(target->channel_info, user->handle_info);
2176 if(!target_user || (target_user->access < UL_OWNER))
2178 reply("CSMSG_MERGE_NOT_OWNER");
2182 /* Merge the channel structures and associated data. */
2183 merge_channel(channel->channel_info, target->channel_info);
2184 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2185 unregister_channel(channel->channel_info, reason);
2186 reply("CSMSG_MERGE_SUCCESS", target->name);
2190 static CHANSERV_FUNC(cmd_opchan)
2192 struct mod_chanmode change;
2193 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2195 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2198 channel->channel_info->may_opchan = 0;
2199 mod_chanmode_init(&change);
2201 change.args[0].mode = MODE_CHANOP;
2202 change.args[0].u.member = GetUserMode(channel, chanserv);
2203 mod_chanmode_announce(chanserv, channel, &change);
2204 reply("CSMSG_OPCHAN_DONE", channel->name);
2208 static CHANSERV_FUNC(cmd_adduser)
2210 struct userData *actee;
2211 struct userData *actor;
2212 struct handle_info *handle;
2213 unsigned short access;
2217 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2219 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2223 access = user_level_from_name(argv[2], UL_OWNER);
2226 reply("CSMSG_INVALID_ACCESS", argv[2]);
2230 actor = GetChannelUser(channel->channel_info, user->handle_info);
2231 if(actor->access <= access)
2233 reply("CSMSG_NO_BUMP_ACCESS");
2237 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2240 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2242 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2246 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2247 scan_user_presence(actee, NULL);
2248 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2252 static CHANSERV_FUNC(cmd_clvl)
2254 struct handle_info *handle;
2255 struct userData *victim;
2256 struct userData *actor;
2257 unsigned short new_access;
2258 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2262 actor = GetChannelUser(channel->channel_info, user->handle_info);
2264 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2267 if(handle == user->handle_info && !privileged)
2269 reply("CSMSG_NO_SELF_CLVL");
2273 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2275 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2279 if(actor->access <= victim->access && !privileged)
2281 reply("MSG_USER_OUTRANKED", handle->handle);
2285 new_access = user_level_from_name(argv[2], UL_OWNER);
2289 reply("CSMSG_INVALID_ACCESS", argv[2]);
2293 if(new_access >= actor->access && !privileged)
2295 reply("CSMSG_NO_BUMP_ACCESS");
2299 victim->access = new_access;
2300 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2304 static CHANSERV_FUNC(cmd_deluser)
2306 struct handle_info *handle;
2307 struct userData *victim;
2308 struct userData *actor;
2309 unsigned short access;
2314 actor = GetChannelUser(channel->channel_info, user->handle_info);
2316 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2319 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2321 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2327 access = user_level_from_name(argv[1], UL_OWNER);
2330 reply("CSMSG_INVALID_ACCESS", argv[1]);
2333 if(access != victim->access)
2335 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2341 access = victim->access;
2344 if((actor->access <= victim->access) && !IsHelping(user))
2346 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2350 chan_name = strdup(channel->name);
2351 del_channel_user(victim, 1);
2352 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2358 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2360 struct userData *actor, *uData, *next;
2362 actor = GetChannelUser(channel->channel_info, user->handle_info);
2364 if(min_access > max_access)
2366 reply("CSMSG_BAD_RANGE", min_access, max_access);
2370 if((actor->access <= max_access) && !IsHelping(user))
2372 reply("CSMSG_NO_ACCESS");
2376 for(uData = channel->channel_info->users; uData; uData = next)
2380 if((uData->access >= min_access)
2381 && (uData->access <= max_access)
2382 && match_ircglob(uData->handle->handle, mask))
2383 del_channel_user(uData, 1);
2386 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2390 static CHANSERV_FUNC(cmd_mdelowner)
2392 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2395 static CHANSERV_FUNC(cmd_mdelcoowner)
2397 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2400 static CHANSERV_FUNC(cmd_mdelmaster)
2402 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2405 static CHANSERV_FUNC(cmd_mdelop)
2407 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2410 static CHANSERV_FUNC(cmd_mdelpeon)
2412 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2416 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2418 struct banData *bData, *next;
2419 char interval[INTERVALLEN];
2424 limit = now - duration;
2425 for(bData = channel->channel_info->bans; bData; bData = next)
2429 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2432 del_channel_ban(bData);
2436 intervalString(interval, duration, user->handle_info);
2437 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2442 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2444 struct userData *actor, *uData, *next;
2445 char interval[INTERVALLEN];
2449 actor = GetChannelUser(channel->channel_info, user->handle_info);
2450 if(min_access > max_access)
2452 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2456 if((actor->access <= max_access) && !IsHelping(user))
2458 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2463 limit = now - duration;
2464 for(uData = channel->channel_info->users; uData; uData = next)
2468 if((uData->seen > limit) || uData->present)
2471 if(((uData->access >= min_access) && (uData->access <= max_access))
2472 || (!max_access && (uData->access < actor->access)))
2474 del_channel_user(uData, 1);
2482 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2484 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2488 static CHANSERV_FUNC(cmd_trim)
2490 unsigned long duration;
2491 unsigned short min_level, max_level;
2495 duration = ParseInterval(argv[2]);
2498 reply("CSMSG_CANNOT_TRIM");
2502 if(!irccasecmp(argv[1], "bans"))
2504 cmd_trim_bans(user, channel, duration);
2507 else if(!irccasecmp(argv[1], "users"))
2509 cmd_trim_users(user, channel, 0, 0, duration);
2512 else if(parse_level_range(&min_level, &max_level, argv[1]))
2514 cmd_trim_users(user, channel, min_level, max_level, duration);
2517 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2519 cmd_trim_users(user, channel, min_level, min_level, duration);
2524 reply("CSMSG_INVALID_TRIM", argv[1]);
2529 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2530 to the user. cmd_all takes advantage of this. */
2531 static CHANSERV_FUNC(cmd_up)
2533 struct mod_chanmode change;
2534 struct userData *uData;
2537 mod_chanmode_init(&change);
2539 change.args[0].u.member = GetUserMode(channel, user);
2540 if(!change.args[0].u.member)
2543 reply("MSG_CHANNEL_ABSENT", channel->name);
2547 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2551 reply("CSMSG_GODMODE_UP", argv[0]);
2554 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2556 change.args[0].mode = MODE_CHANOP;
2557 errmsg = "CSMSG_ALREADY_OPPED";
2559 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2561 change.args[0].mode = MODE_VOICE;
2562 errmsg = "CSMSG_ALREADY_VOICED";
2567 reply("CSMSG_NO_ACCESS");
2570 change.args[0].mode &= ~change.args[0].u.member->modes;
2571 if(!change.args[0].mode)
2574 reply(errmsg, channel->name);
2577 modcmd_chanmode_announce(&change);
2581 static CHANSERV_FUNC(cmd_down)
2583 struct mod_chanmode change;
2585 mod_chanmode_init(&change);
2587 change.args[0].u.member = GetUserMode(channel, user);
2588 if(!change.args[0].u.member)
2591 reply("MSG_CHANNEL_ABSENT", channel->name);
2595 if(!change.args[0].u.member->modes)
2598 reply("CSMSG_ALREADY_DOWN", channel->name);
2602 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2603 modcmd_chanmode_announce(&change);
2607 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)
2609 struct userData *cList;
2611 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2613 if(IsSuspended(cList->channel)
2614 || IsUserSuspended(cList)
2615 || !GetUserMode(cList->channel->channel, user))
2618 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2624 static CHANSERV_FUNC(cmd_upall)
2626 return cmd_all(CSFUNC_ARGS, cmd_up);
2629 static CHANSERV_FUNC(cmd_downall)
2631 return cmd_all(CSFUNC_ARGS, cmd_down);
2634 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2635 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2638 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)
2640 unsigned int ii, valid;
2641 struct userNode *victim;
2642 struct mod_chanmode *change;
2644 change = mod_chanmode_alloc(argc - 1);
2646 for(ii=valid=0; ++ii < argc; )
2648 if(!(victim = GetUserH(argv[ii])))
2650 change->args[valid].mode = mode;
2651 change->args[valid].u.member = GetUserMode(channel, victim);
2652 if(!change->args[valid].u.member)
2654 if(validate && !validate(user, channel, victim))
2659 change->argc = valid;
2660 if(valid < (argc-1))
2661 reply("CSMSG_PROCESS_FAILED");
2664 modcmd_chanmode_announce(change);
2665 reply(action, channel->name);
2667 mod_chanmode_free(change);
2671 static CHANSERV_FUNC(cmd_op)
2673 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2676 static CHANSERV_FUNC(cmd_deop)
2678 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2681 static CHANSERV_FUNC(cmd_voice)
2683 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2686 static CHANSERV_FUNC(cmd_devoice)
2688 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2692 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2698 for(ii=0; ii<channel->members.used; ii++)
2700 struct modeNode *mn = channel->members.list[ii];
2702 if(IsService(mn->user))
2705 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2708 if(protect_user(mn->user, user, channel->channel_info))
2712 victims[(*victimCount)++] = mn;
2718 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2720 struct userNode *victim;
2721 struct modeNode **victims;
2722 unsigned int offset, n, victimCount, duration = 0;
2723 char *reason = "Bye.", *ban, *name;
2724 char interval[INTERVALLEN];
2726 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2727 REQUIRE_PARAMS(offset);
2730 reason = unsplit_string(argv + offset, argc - offset, NULL);
2731 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2733 /* Truncate the reason to a length of TOPICLEN, as
2734 the ircd does; however, leave room for an ellipsis
2735 and the kicker's nick. */
2736 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2740 if((victim = GetUserH(argv[1])))
2742 victims = alloca(sizeof(victims[0]));
2743 victims[0] = GetUserMode(channel, victim);
2744 /* XXX: The comparison with ACTION_KICK is just because all
2745 * other actions can work on users outside the channel, and we
2746 * want to allow those (e.g. unbans) in that case. If we add
2747 * some other ejection action for in-channel users, change
2749 victimCount = victims[0] ? 1 : 0;
2751 if(IsService(victim))
2753 reply("MSG_SERVICE_IMMUNE", victim->nick);
2757 if((action == ACTION_KICK) && !victimCount)
2759 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2763 if(protect_user(victim, user, channel->channel_info))
2765 reply("CSMSG_USER_PROTECTED", victim->nick);
2769 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2770 name = victim->nick;
2774 if(!is_ircmask(argv[1]))
2776 reply("MSG_NICK_UNKNOWN", argv[1]);
2780 victims = alloca(sizeof(victims[0]) * channel->members.used);
2782 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2784 reply("CSMSG_MASK_PROTECTED", argv[1]);
2788 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2790 reply("CSMSG_LAME_MASK", argv[1]);
2794 if((action == ACTION_KICK) && (victimCount == 0))
2796 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2800 name = ban = strdup(argv[1]);
2803 /* Truncate the ban in place if necessary; we must ensure
2804 that 'ban' is a valid ban mask before sanitizing it. */
2805 sanitize_ircmask(ban);
2807 if(action & ACTION_ADD_BAN)
2809 struct banData *bData, *next;
2811 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2813 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2818 if(action & ACTION_ADD_TIMED_BAN)
2820 duration = ParseInterval(argv[2]);
2824 reply("CSMSG_DURATION_TOO_LOW");
2828 else if(duration > (86400 * 365 * 2))
2830 reply("CSMSG_DURATION_TOO_HIGH");
2836 for(bData = channel->channel_info->bans; bData; bData = next)
2838 if(match_ircglobs(bData->mask, ban))
2840 int exact = !irccasecmp(bData->mask, ban);
2842 /* The ban is redundant; there is already a ban
2843 with the same effect in place. */
2847 free(bData->reason);
2848 bData->reason = strdup(reason);
2849 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2851 reply("CSMSG_REASON_CHANGE", ban);
2855 if(exact && bData->expires)
2859 /* If the ban matches an existing one exactly,
2860 extend the expiration time if the provided
2861 duration is longer. */
2862 if(duration && ((time_t)(now + duration) > bData->expires))
2864 bData->expires = now + duration;
2875 /* Delete the expiration timeq entry and
2876 requeue if necessary. */
2877 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2880 timeq_add(bData->expires, expire_ban, bData);
2884 /* automated kickban */
2887 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2889 reply("CSMSG_BAN_ADDED", name, channel->name);
2895 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2902 if(match_ircglobs(ban, bData->mask))
2904 /* The ban we are adding makes previously existing
2905 bans redundant; silently remove them. */
2906 del_channel_ban(bData);
2910 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);
2912 name = ban = strdup(bData->mask);
2916 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2918 extern const char *hidden_host_suffix;
2919 const char *old_name = chanserv_conf.old_ban_names->list[n];
2921 unsigned int l1, l2;
2924 l2 = strlen(old_name);
2927 if(irccasecmp(ban + l1 - l2, old_name))
2929 new_mask = malloc(MAXLEN);
2930 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2932 name = ban = new_mask;
2937 if(action & ACTION_BAN)
2939 unsigned int exists;
2940 struct mod_chanmode *change;
2942 if(channel->banlist.used >= MAXBANS)
2945 reply("CSMSG_BANLIST_FULL", channel->name);
2950 exists = ChannelBanExists(channel, ban);
2951 change = mod_chanmode_alloc(victimCount + 1);
2952 for(n = 0; n < victimCount; ++n)
2954 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2955 change->args[n].u.member = victims[n];
2959 change->args[n].mode = MODE_BAN;
2960 change->args[n++].u.hostmask = ban;
2964 modcmd_chanmode_announce(change);
2966 mod_chanmode_announce(chanserv, channel, change);
2967 mod_chanmode_free(change);
2969 if(exists && (action == ACTION_BAN))
2972 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2978 if(action & ACTION_KICK)
2980 char kick_reason[MAXLEN];
2981 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2983 for(n = 0; n < victimCount; n++)
2984 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2989 /* No response, since it was automated. */
2991 else if(action & ACTION_ADD_BAN)
2994 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2996 reply("CSMSG_BAN_ADDED", name, channel->name);
2998 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2999 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3000 else if(action & ACTION_BAN)
3001 reply("CSMSG_BAN_DONE", name, channel->name);
3002 else if(action & ACTION_KICK && victimCount)
3003 reply("CSMSG_KICK_DONE", name, channel->name);
3009 static CHANSERV_FUNC(cmd_kickban)
3011 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3014 static CHANSERV_FUNC(cmd_kick)
3016 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3019 static CHANSERV_FUNC(cmd_ban)
3021 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3024 static CHANSERV_FUNC(cmd_addban)
3026 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3029 static CHANSERV_FUNC(cmd_addtimedban)
3031 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3034 static struct mod_chanmode *
3035 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3037 struct mod_chanmode *change;
3038 unsigned char *match;
3039 unsigned int ii, count;
3041 match = alloca(bans->used);
3044 for(ii = count = 0; ii < bans->used; ++ii)
3046 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3047 MATCH_USENICK | MATCH_VISIBLE);
3054 for(ii = count = 0; ii < bans->used; ++ii)
3056 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3063 change = mod_chanmode_alloc(count);
3064 for(ii = count = 0; ii < bans->used; ++ii)
3068 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3069 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3071 assert(count == change->argc);
3076 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3078 struct userNode *actee;
3084 /* may want to allow a comma delimited list of users... */
3085 if(!(actee = GetUserH(argv[1])))
3087 if(!is_ircmask(argv[1]))
3089 reply("MSG_NICK_UNKNOWN", argv[1]);
3093 mask = strdup(argv[1]);
3096 /* We don't sanitize the mask here because ircu
3098 if(action & ACTION_UNBAN)
3100 struct mod_chanmode *change;
3101 change = find_matching_bans(&channel->banlist, actee, mask);
3106 modcmd_chanmode_announce(change);
3107 for(ii = 0; ii < change->argc; ++ii)
3108 free((char*)change->args[ii].u.hostmask);
3109 mod_chanmode_free(change);
3114 if(action & ACTION_DEL_BAN)
3116 struct banData *ban, *next;
3118 ban = channel->channel_info->bans;
3122 for( ; ban && !user_matches_glob(actee, ban->mask,
3123 MATCH_USENICK | MATCH_VISIBLE);
3126 for( ; ban && !match_ircglobs(mask, ban->mask);
3131 del_channel_ban(ban);
3138 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3140 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3146 static CHANSERV_FUNC(cmd_unban)
3148 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3151 static CHANSERV_FUNC(cmd_delban)
3153 /* it doesn't necessarily have to remove the channel ban - may want
3154 to make that an option. */
3155 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3158 static CHANSERV_FUNC(cmd_unbanme)
3160 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3161 long flags = ACTION_UNBAN;
3163 /* remove permanent bans if the user has the proper access. */
3164 if(uData->access >= UL_MASTER)
3165 flags |= ACTION_DEL_BAN;
3167 argv[1] = user->nick;
3168 return unban_user(user, channel, 2, argv, cmd, flags);
3171 static CHANSERV_FUNC(cmd_unbanall)
3173 struct mod_chanmode *change;
3176 if(!channel->banlist.used)
3178 reply("CSMSG_NO_BANS", channel->name);
3182 change = mod_chanmode_alloc(channel->banlist.used);
3183 for(ii=0; ii<channel->banlist.used; ii++)
3185 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3186 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3188 modcmd_chanmode_announce(change);
3189 for(ii = 0; ii < change->argc; ++ii)
3190 free((char*)change->args[ii].u.hostmask);
3191 mod_chanmode_free(change);
3192 reply("CSMSG_BANS_REMOVED", channel->name);
3196 static CHANSERV_FUNC(cmd_open)
3198 struct mod_chanmode *change;
3201 change = find_matching_bans(&channel->banlist, user, NULL);
3203 change = mod_chanmode_alloc(0);
3204 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3205 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3206 && channel->channel_info->modes.modes_set)
3207 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3208 modcmd_chanmode_announce(change);
3209 reply("CSMSG_CHANNEL_OPENED", channel->name);
3210 for(ii = 0; ii < change->argc; ++ii)
3211 free((char*)change->args[ii].u.hostmask);
3212 mod_chanmode_free(change);
3216 static CHANSERV_FUNC(cmd_myaccess)
3218 static struct string_buffer sbuf;
3219 struct handle_info *target_handle;
3220 struct userData *uData;
3223 target_handle = user->handle_info;
3224 else if(!IsHelping(user))
3226 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3229 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3232 if(!target_handle->channels)
3234 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3238 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3239 for(uData = target_handle->channels; uData; uData = uData->u_next)
3241 struct chanData *cData = uData->channel;
3243 if(uData->access > UL_OWNER)
3245 if(IsProtected(cData)
3246 && (target_handle != user->handle_info)
3247 && !GetTrueChannelAccess(cData, user->handle_info))
3250 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3251 if(uData->flags != USER_AUTO_OP)
3252 string_buffer_append(&sbuf, ',');
3253 if(IsUserSuspended(uData))
3254 string_buffer_append(&sbuf, 's');
3255 if(IsUserAutoOp(uData))
3257 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3258 string_buffer_append(&sbuf, 'o');
3259 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3260 string_buffer_append(&sbuf, 'v');
3262 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3263 string_buffer_append(&sbuf, 'i');
3265 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3267 string_buffer_append_string(&sbuf, ")]");
3268 string_buffer_append(&sbuf, '\0');
3269 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3275 static CHANSERV_FUNC(cmd_access)
3277 struct userNode *target;
3278 struct handle_info *target_handle;
3279 struct userData *uData;
3281 char prefix[MAXLEN];
3286 target_handle = target->handle_info;
3288 else if((target = GetUserH(argv[1])))
3290 target_handle = target->handle_info;
3292 else if(argv[1][0] == '*')
3294 if(!(target_handle = get_handle_info(argv[1]+1)))
3296 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3302 reply("MSG_NICK_UNKNOWN", argv[1]);
3306 assert(target || target_handle);
3308 if(target == chanserv)
3310 reply("CSMSG_IS_CHANSERV");
3318 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3323 reply("MSG_USER_AUTHENTICATE", target->nick);
3326 reply("MSG_AUTHENTICATE");
3332 const char *epithet = NULL, *type = NULL;
3335 epithet = chanserv_conf.irc_operator_epithet;
3338 else if(IsNetworkHelper(target))
3340 epithet = chanserv_conf.network_helper_epithet;
3341 type = "network helper";
3343 else if(IsSupportHelper(target))
3345 epithet = chanserv_conf.support_helper_epithet;
3346 type = "support helper";
3350 if(target_handle->epithet)
3351 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3353 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3355 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3359 sprintf(prefix, "%s", target_handle->handle);
3362 if(!channel->channel_info)
3364 reply("CSMSG_NOT_REGISTERED", channel->name);
3368 helping = HANDLE_FLAGGED(target_handle, HELPING)
3369 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3370 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3372 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3373 /* To prevent possible information leaks, only show infolines
3374 * if the requestor is in the channel or it's their own
3376 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3378 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3380 /* Likewise, only say it's suspended if the user has active
3381 * access in that channel or it's their own entry. */
3382 if(IsUserSuspended(uData)
3383 && (GetChannelUser(channel->channel_info, user->handle_info)
3384 || (user->handle_info == uData->handle)))
3386 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3391 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3398 zoot_list(struct listData *list)
3400 struct userData *uData;
3401 unsigned int start, curr, highest, lowest;
3402 struct helpfile_table tmp_table;
3403 const char **temp, *msg;
3405 if(list->table.length == 1)
3408 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3410 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3411 msg = user_find_message(list->user, "MSG_NONE");
3412 send_message_type(4, list->user, list->bot, " %s", msg);
3414 tmp_table.width = list->table.width;
3415 tmp_table.flags = list->table.flags;
3416 list->table.contents[0][0] = " ";
3417 highest = list->highest;
3418 if(list->lowest != 0)
3419 lowest = list->lowest;
3420 else if(highest < 100)
3423 lowest = highest - 100;
3424 for(start = curr = 1; curr < list->table.length; )
3426 uData = list->users[curr-1];
3427 list->table.contents[curr++][0] = " ";
3428 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3431 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3433 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3434 temp = list->table.contents[--start];
3435 list->table.contents[start] = list->table.contents[0];
3436 tmp_table.contents = list->table.contents + start;
3437 tmp_table.length = curr - start;
3438 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3439 list->table.contents[start] = temp;
3441 highest = lowest - 1;
3442 lowest = (highest < 100) ? 0 : (highest - 99);
3448 def_list(struct listData *list)
3452 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3454 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3455 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3456 if(list->table.length == 1)
3458 msg = user_find_message(list->user, "MSG_NONE");
3459 send_message_type(4, list->user, list->bot, " %s", msg);
3464 userData_access_comp(const void *arg_a, const void *arg_b)
3466 const struct userData *a = *(struct userData**)arg_a;
3467 const struct userData *b = *(struct userData**)arg_b;
3469 if(a->access != b->access)
3470 res = b->access - a->access;
3472 res = irccasecmp(a->handle->handle, b->handle->handle);
3477 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3479 void (*send_list)(struct listData *);
3480 struct userData *uData;
3481 struct listData lData;
3482 unsigned int matches;
3486 lData.bot = cmd->parent->bot;
3487 lData.channel = channel;
3488 lData.lowest = lowest;
3489 lData.highest = highest;
3490 lData.search = (argc > 1) ? argv[1] : NULL;
3491 send_list = def_list;
3492 (void)zoot_list; /* since it doesn't show user levels */
3494 if(user->handle_info)
3496 switch(user->handle_info->userlist_style)
3498 case HI_STYLE_DEF: send_list = def_list; break;
3499 case HI_STYLE_ZOOT: send_list = def_list; break;
3503 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3505 for(uData = channel->channel_info->users; uData; uData = uData->next)
3507 if((uData->access < lowest)
3508 || (uData->access > highest)
3509 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3511 lData.users[matches++] = uData;
3513 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3515 lData.table.length = matches+1;
3516 lData.table.width = 4;
3517 lData.table.flags = TABLE_NO_FREE;
3518 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3519 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3520 lData.table.contents[0] = ary;
3523 ary[2] = "Last Seen";
3525 for(matches = 1; matches < lData.table.length; ++matches)
3527 struct userData *uData = lData.users[matches-1];
3528 char seen[INTERVALLEN];
3530 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3531 lData.table.contents[matches] = ary;
3532 ary[0] = strtab(uData->access);
3533 ary[1] = uData->handle->handle;
3536 else if(!uData->seen)
3539 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3540 ary[2] = strdup(ary[2]);
3541 if(IsUserSuspended(uData))
3542 ary[3] = "Suspended";
3543 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3544 ary[3] = "Vacation";
3549 for(matches = 1; matches < lData.table.length; ++matches)
3551 free((char*)lData.table.contents[matches][2]);
3552 free(lData.table.contents[matches]);
3554 free(lData.table.contents[0]);
3555 free(lData.table.contents);
3559 static CHANSERV_FUNC(cmd_users)
3561 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3564 static CHANSERV_FUNC(cmd_wlist)
3566 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3569 static CHANSERV_FUNC(cmd_clist)
3571 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3574 static CHANSERV_FUNC(cmd_mlist)
3576 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3579 static CHANSERV_FUNC(cmd_olist)
3581 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3584 static CHANSERV_FUNC(cmd_plist)
3586 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3589 static CHANSERV_FUNC(cmd_bans)
3591 struct helpfile_table tbl;
3592 unsigned int matches = 0, timed = 0, ii;
3593 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3594 const char *msg_never, *triggered, *expires;
3595 struct banData *ban, **bans;
3602 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3604 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3606 if(search && !match_ircglobs(search, ban->mask))
3608 bans[matches++] = ban;
3613 tbl.length = matches + 1;
3614 tbl.width = 4 + timed;
3616 tbl.flags = TABLE_NO_FREE;
3617 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3618 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3619 tbl.contents[0][0] = "Mask";
3620 tbl.contents[0][1] = "Set By";
3621 tbl.contents[0][2] = "Triggered";
3624 tbl.contents[0][3] = "Expires";
3625 tbl.contents[0][4] = "Reason";
3628 tbl.contents[0][3] = "Reason";
3631 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3633 free(tbl.contents[0]);
3638 msg_never = user_find_message(user, "MSG_NEVER");
3639 for(ii = 0; ii < matches; )
3645 else if(ban->expires)
3646 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3648 expires = msg_never;
3651 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3653 triggered = msg_never;
3655 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3656 tbl.contents[ii][0] = ban->mask;
3657 tbl.contents[ii][1] = ban->owner;
3658 tbl.contents[ii][2] = strdup(triggered);
3661 tbl.contents[ii][3] = strdup(expires);
3662 tbl.contents[ii][4] = ban->reason;
3665 tbl.contents[ii][3] = ban->reason;
3667 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3668 reply("MSG_MATCH_COUNT", matches);
3669 for(ii = 1; ii < tbl.length; ++ii)
3671 free((char*)tbl.contents[ii][2]);
3673 free((char*)tbl.contents[ii][3]);
3674 free(tbl.contents[ii]);
3676 free(tbl.contents[0]);
3682 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3684 struct chanData *cData = channel->channel_info;
3685 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3687 if(cData->topic_mask)
3688 return !match_ircglob(new_topic, cData->topic_mask);
3689 else if(cData->topic)
3690 return irccasecmp(new_topic, cData->topic);
3695 static CHANSERV_FUNC(cmd_topic)
3697 struct chanData *cData;
3700 cData = channel->channel_info;
3705 SetChannelTopic(channel, chanserv, cData->topic, 1);
3706 reply("CSMSG_TOPIC_SET", cData->topic);
3710 reply("CSMSG_NO_TOPIC", channel->name);
3714 topic = unsplit_string(argv + 1, argc - 1, NULL);
3715 /* If they say "!topic *", use an empty topic. */
3716 if((topic[0] == '*') && (topic[1] == 0))
3718 if(bad_topic(channel, user, topic))
3720 char *topic_mask = cData->topic_mask;
3723 char new_topic[TOPICLEN+1], tchar;
3724 int pos=0, starpos=-1, dpos=0, len;
3726 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3733 len = strlen(topic);
3734 if((dpos + len) > TOPICLEN)
3735 len = TOPICLEN + 1 - dpos;
3736 memcpy(new_topic+dpos, topic, len);
3740 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3741 default: new_topic[dpos++] = tchar; break;
3744 if((dpos > TOPICLEN) || tchar)
3747 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3748 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3751 new_topic[dpos] = 0;
3752 SetChannelTopic(channel, chanserv, new_topic, 1);
3754 reply("CSMSG_TOPIC_LOCKED", channel->name);
3759 SetChannelTopic(channel, chanserv, topic, 1);
3761 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3763 /* Grab the topic and save it as the default topic. */
3765 cData->topic = strdup(channel->topic);
3771 static CHANSERV_FUNC(cmd_mode)
3773 struct userData *uData;
3774 struct mod_chanmode *change;
3779 change = &channel->channel_info->modes;
3780 if(change->modes_set || change->modes_clear) {
3781 modcmd_chanmode_announce(change);
3782 reply("CSMSG_DEFAULTED_MODES", channel->name);
3784 reply("CSMSG_NO_MODES", channel->name);
3788 uData = GetChannelUser(channel->channel_info, user->handle_info);
3790 base_oplevel = MAXOPLEVEL;
3791 else if (uData->access >= UL_OWNER)
3794 base_oplevel = 1 + UL_OWNER - uData->access;
3795 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3798 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3802 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3803 && mode_lock_violated(&channel->channel_info->modes, change))
3806 mod_chanmode_format(&channel->channel_info->modes, modes);
3807 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3811 modcmd_chanmode_announce(change);
3812 mod_chanmode_free(change);
3813 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3817 static CHANSERV_FUNC(cmd_invite)
3819 struct userData *uData;
3820 struct userNode *invite;
3822 uData = GetChannelUser(channel->channel_info, user->handle_info);
3826 if(!(invite = GetUserH(argv[1])))
3828 reply("MSG_NICK_UNKNOWN", argv[1]);
3835 if(GetUserMode(channel, invite))
3837 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3845 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3846 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3849 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3851 irc_invite(chanserv, invite, channel);
3853 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3858 static CHANSERV_FUNC(cmd_inviteme)
3860 if(GetUserMode(channel, user))
3862 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3865 if(channel->channel_info
3866 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3868 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3871 irc_invite(cmd->parent->bot, user, channel);
3876 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3879 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3881 /* We display things based on two dimensions:
3882 * - Issue time: present or absent
3883 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3884 * (in order of precedence, so something both expired and revoked
3885 * only counts as revoked)
3887 combo = (suspended->issued ? 4 : 0)
3888 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3890 case 0: /* no issue time, indefinite expiration */
3891 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3893 case 1: /* no issue time, expires in future */
3894 intervalString(buf1, suspended->expires-now, user->handle_info);
3895 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3897 case 2: /* no issue time, expired */
3898 intervalString(buf1, now-suspended->expires, user->handle_info);
3899 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3901 case 3: /* no issue time, revoked */
3902 intervalString(buf1, now-suspended->revoked, user->handle_info);
3903 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3905 case 4: /* issue time set, indefinite expiration */
3906 intervalString(buf1, now-suspended->issued, user->handle_info);
3907 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3909 case 5: /* issue time set, expires in future */
3910 intervalString(buf1, now-suspended->issued, user->handle_info);
3911 intervalString(buf2, suspended->expires-now, user->handle_info);
3912 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3914 case 6: /* issue time set, expired */
3915 intervalString(buf1, now-suspended->issued, user->handle_info);
3916 intervalString(buf2, now-suspended->expires, user->handle_info);
3917 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3919 case 7: /* issue time set, revoked */
3920 intervalString(buf1, now-suspended->issued, user->handle_info);
3921 intervalString(buf2, now-suspended->revoked, user->handle_info);
3922 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3925 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3930 static CHANSERV_FUNC(cmd_info)
3932 char modes[MAXLEN], buffer[INTERVALLEN];
3933 struct userData *uData, *owner;
3934 struct chanData *cData;
3935 struct do_not_register *dnr;
3940 cData = channel->channel_info;
3941 reply("CSMSG_CHANNEL_INFO", channel->name);
3943 uData = GetChannelUser(cData, user->handle_info);
3944 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3946 mod_chanmode_format(&cData->modes, modes);
3947 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3948 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3951 for(it = dict_first(cData->notes); it; it = iter_next(it))
3955 note = iter_data(it);
3956 if(!note_type_visible_to_user(cData, note->type, user))
3959 padding = PADLEN - 1 - strlen(iter_key(it));
3960 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3963 reply("CSMSG_CHANNEL_MAX", cData->max);
3964 for(owner = cData->users; owner; owner = owner->next)
3965 if(owner->access == UL_OWNER)
3966 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3967 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3968 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3969 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3971 privileged = IsStaff(user);
3973 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3974 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3975 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3977 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3978 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3980 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3982 struct suspended *suspended;
3983 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3984 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3985 show_suspension_info(cmd, user, suspended);
3987 else if(IsSuspended(cData))
3989 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3990 show_suspension_info(cmd, user, cData->suspended);
3995 static CHANSERV_FUNC(cmd_netinfo)
3997 extern time_t boot_time;
3998 extern unsigned long burst_length;
3999 char interval[INTERVALLEN];
4001 reply("CSMSG_NETWORK_INFO");
4002 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4003 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4004 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4005 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4006 reply("CSMSG_NETWORK_BANS", banCount);
4007 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4008 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4009 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4014 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4016 struct helpfile_table table;
4018 struct userNode *user;
4023 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4024 table.contents = alloca(list->used*sizeof(*table.contents));
4025 for(nn=0; nn<list->used; nn++)
4027 user = list->list[nn];
4028 if(user->modes & skip_flags)
4032 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4035 nick = alloca(strlen(user->nick)+3);
4036 sprintf(nick, "(%s)", user->nick);
4040 table.contents[table.length][0] = nick;
4043 table_send(chanserv, to->nick, 0, NULL, table);
4046 static CHANSERV_FUNC(cmd_ircops)
4048 reply("CSMSG_STAFF_OPERS");
4049 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4053 static CHANSERV_FUNC(cmd_helpers)
4055 reply("CSMSG_STAFF_HELPERS");
4056 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4060 static CHANSERV_FUNC(cmd_staff)
4062 reply("CSMSG_NETWORK_STAFF");
4063 cmd_ircops(CSFUNC_ARGS);
4064 cmd_helpers(CSFUNC_ARGS);
4068 static CHANSERV_FUNC(cmd_peek)
4070 struct modeNode *mn;
4071 char modes[MODELEN];
4073 struct helpfile_table table;
4075 irc_make_chanmode(channel, modes);
4077 reply("CSMSG_PEEK_INFO", channel->name);
4078 reply("CSMSG_PEEK_TOPIC", channel->topic);
4079 reply("CSMSG_PEEK_MODES", modes);
4080 reply("CSMSG_PEEK_USERS", channel->members.used);
4084 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4085 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4086 for(n = 0; n < channel->members.used; n++)
4088 mn = channel->members.list[n];
4089 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4091 table.contents[table.length] = alloca(sizeof(**table.contents));
4092 table.contents[table.length][0] = mn->user->nick;
4097 reply("CSMSG_PEEK_OPS");
4098 table_send(chanserv, user->nick, 0, NULL, table);
4101 reply("CSMSG_PEEK_NO_OPS");
4105 static MODCMD_FUNC(cmd_wipeinfo)
4107 struct handle_info *victim;
4108 struct userData *ud, *actor;
4111 actor = GetChannelUser(channel->channel_info, user->handle_info);
4112 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4114 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4116 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4119 if((ud->access >= actor->access) && (ud != actor))
4121 reply("MSG_USER_OUTRANKED", victim->handle);
4127 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4131 static CHANSERV_FUNC(cmd_resync)
4133 struct mod_chanmode *changes;
4134 struct chanData *cData = channel->channel_info;
4135 unsigned int ii, used;
4137 changes = mod_chanmode_alloc(channel->members.used * 2);
4138 for(ii = used = 0; ii < channel->members.used; ++ii)
4140 struct modeNode *mn = channel->members.list[ii];
4141 struct userData *uData;
4143 if(IsService(mn->user))
4146 uData = GetChannelAccess(cData, mn->user->handle_info);
4147 if(!cData->lvlOpts[lvlGiveOps]
4148 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4150 if(!(mn->modes & MODE_CHANOP))
4152 changes->args[used].mode = MODE_CHANOP;
4153 changes->args[used++].u.member = mn;
4156 else if(!cData->lvlOpts[lvlGiveVoice]
4157 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4159 if(mn->modes & MODE_CHANOP)
4161 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4162 changes->args[used++].u.member = mn;
4164 if(!(mn->modes & MODE_VOICE))
4166 changes->args[used].mode = MODE_VOICE;
4167 changes->args[used++].u.member = mn;
4174 changes->args[used].mode = MODE_REMOVE | mn->modes;
4175 changes->args[used++].u.member = mn;
4179 changes->argc = used;
4180 modcmd_chanmode_announce(changes);
4181 mod_chanmode_free(changes);
4182 reply("CSMSG_RESYNCED_USERS", channel->name);
4186 static CHANSERV_FUNC(cmd_seen)
4188 struct userData *uData;
4189 struct handle_info *handle;
4190 char seen[INTERVALLEN];
4194 if(!irccasecmp(argv[1], chanserv->nick))
4196 reply("CSMSG_IS_CHANSERV");
4200 if(!(handle = get_handle_info(argv[1])))
4202 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4206 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4208 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4213 reply("CSMSG_USER_PRESENT", handle->handle);
4214 else if(uData->seen)
4215 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4217 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4219 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4220 reply("CSMSG_USER_VACATION", handle->handle);
4225 static MODCMD_FUNC(cmd_names)
4227 struct userNode *targ;
4228 struct userData *targData;
4229 unsigned int ii, pos;
4232 for(ii=pos=0; ii<channel->members.used; ++ii)
4234 targ = channel->members.list[ii]->user;
4235 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4238 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4241 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4245 if(IsUserSuspended(targData))
4247 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4250 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4251 reply("CSMSG_END_NAMES", channel->name);
4256 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4258 switch(ntype->visible_type)
4260 case NOTE_VIS_ALL: return 1;
4261 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4262 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4267 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4269 struct userData *uData;
4271 switch(ntype->set_access_type)
4273 case NOTE_SET_CHANNEL_ACCESS:
4274 if(!user->handle_info)
4276 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4278 return uData->access >= ntype->set_access.min_ulevel;
4279 case NOTE_SET_CHANNEL_SETTER:
4280 return check_user_level(channel, user, lvlSetters, 1, 0);
4281 case NOTE_SET_PRIVILEGED: default:
4282 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4286 static CHANSERV_FUNC(cmd_note)
4288 struct chanData *cData;
4290 struct note_type *ntype;
4292 cData = channel->channel_info;
4295 reply("CSMSG_NOT_REGISTERED", channel->name);
4299 /* If no arguments, show all visible notes for the channel. */
4305 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4307 note = iter_data(it);
4308 if(!note_type_visible_to_user(cData, note->type, user))
4311 reply("CSMSG_NOTELIST_HEADER", channel->name);
4312 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4315 reply("CSMSG_NOTELIST_END", channel->name);
4317 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4319 /* If one argument, show the named note. */
4322 if((note = dict_find(cData->notes, argv[1], NULL))
4323 && note_type_visible_to_user(cData, note->type, user))
4325 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4327 else if((ntype = dict_find(note_types, argv[1], NULL))
4328 && note_type_visible_to_user(NULL, ntype, user))
4330 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4335 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4339 /* Assume they're trying to set a note. */
4343 ntype = dict_find(note_types, argv[1], NULL);
4346 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4349 else if(note_type_settable_by_user(channel, ntype, user))
4351 note_text = unsplit_string(argv+2, argc-2, NULL);
4352 if((note = dict_find(cData->notes, argv[1], NULL)))
4353 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4354 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4355 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4357 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4359 /* The note is viewable to staff only, so return 0
4360 to keep the invocation from getting logged (or
4361 regular users can see it in !events). */
4367 reply("CSMSG_NO_ACCESS");
4374 static CHANSERV_FUNC(cmd_delnote)
4379 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4380 || !note_type_settable_by_user(channel, note->type, user))
4382 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4385 dict_remove(channel->channel_info->notes, note->type->name);
4386 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4390 static CHANSERV_FUNC(cmd_events)
4392 struct logSearch discrim;
4393 struct logReport report;
4394 unsigned int matches, limit;
4396 limit = (argc > 1) ? atoi(argv[1]) : 10;
4397 if(limit < 1 || limit > 200)
4400 memset(&discrim, 0, sizeof(discrim));
4401 discrim.masks.bot = chanserv;
4402 discrim.masks.channel_name = channel->name;
4404 discrim.masks.command = argv[2];
4405 discrim.limit = limit;
4406 discrim.max_time = INT_MAX;
4407 discrim.severities = 1 << LOG_COMMAND;
4408 report.reporter = chanserv;
4410 reply("CSMSG_EVENT_SEARCH_RESULTS");
4411 matches = log_entry_search(&discrim, log_report_entry, &report);
4413 reply("MSG_MATCH_COUNT", matches);
4415 reply("MSG_NO_MATCHES");
4419 static CHANSERV_FUNC(cmd_say)
4425 msg = unsplit_string(argv + 1, argc - 1, NULL);
4426 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4428 else if(GetUserH(argv[1]))
4431 msg = unsplit_string(argv + 2, argc - 2, NULL);
4432 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4436 reply("MSG_NOT_TARGET_NAME");
4442 static CHANSERV_FUNC(cmd_emote)
4448 /* CTCP is so annoying. */
4449 msg = unsplit_string(argv + 1, argc - 1, NULL);
4450 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4452 else if(GetUserH(argv[1]))
4454 msg = unsplit_string(argv + 2, argc - 2, NULL);
4455 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4459 reply("MSG_NOT_TARGET_NAME");
4465 struct channelList *
4466 chanserv_support_channels(void)
4468 return &chanserv_conf.support_channels;
4471 static CHANSERV_FUNC(cmd_expire)
4473 int channel_count = registered_channels;
4474 expire_channels(NULL);
4475 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4480 chanserv_expire_suspension(void *data)
4482 struct suspended *suspended = data;
4483 struct chanNode *channel;
4485 if(!suspended->expires || (now < suspended->expires))
4486 suspended->revoked = now;
4487 channel = suspended->cData->channel;
4488 suspended->cData->channel = channel;
4489 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4490 if(!IsOffChannel(suspended->cData))
4492 struct mod_chanmode change;
4493 mod_chanmode_init(&change);
4495 change.args[0].mode = MODE_CHANOP;
4496 change.args[0].u.member = AddChannelUser(chanserv, channel);
4497 mod_chanmode_announce(chanserv, channel, &change);
4501 static CHANSERV_FUNC(cmd_csuspend)
4503 struct suspended *suspended;
4504 char reason[MAXLEN];
4505 time_t expiry, duration;
4506 struct userData *uData;
4510 if(IsProtected(channel->channel_info))
4512 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4516 if(argv[1][0] == '!')
4518 else if(IsSuspended(channel->channel_info))
4520 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4521 show_suspension_info(cmd, user, channel->channel_info->suspended);
4525 if(!strcmp(argv[1], "0"))
4527 else if((duration = ParseInterval(argv[1])))
4528 expiry = now + duration;
4531 reply("MSG_INVALID_DURATION", argv[1]);
4535 unsplit_string(argv + 2, argc - 2, reason);
4537 suspended = calloc(1, sizeof(*suspended));
4538 suspended->revoked = 0;
4539 suspended->issued = now;
4540 suspended->suspender = strdup(user->handle_info->handle);
4541 suspended->expires = expiry;
4542 suspended->reason = strdup(reason);
4543 suspended->cData = channel->channel_info;
4544 suspended->previous = suspended->cData->suspended;
4545 suspended->cData->suspended = suspended;
4547 if(suspended->expires)
4548 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4550 if(IsSuspended(channel->channel_info))
4552 suspended->previous->revoked = now;
4553 if(suspended->previous->expires)
4554 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4555 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4556 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4560 /* Mark all users in channel as absent. */
4561 for(uData = channel->channel_info->users; uData; uData = uData->next)
4570 /* Mark the channel as suspended, then part. */
4571 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4572 DelChannelUser(chanserv, channel, suspended->reason, 0);
4573 reply("CSMSG_SUSPENDED", channel->name);
4574 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4575 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4580 static CHANSERV_FUNC(cmd_cunsuspend)
4582 struct suspended *suspended;
4583 char message[MAXLEN];
4585 if(!IsSuspended(channel->channel_info))
4587 reply("CSMSG_NOT_SUSPENDED", channel->name);
4591 suspended = channel->channel_info->suspended;
4593 /* Expire the suspension and join ChanServ to the channel. */
4594 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4595 chanserv_expire_suspension(suspended);
4596 reply("CSMSG_UNSUSPENDED", channel->name);
4597 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4598 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4602 typedef struct chanservSearch
4610 unsigned long flags;
4614 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4617 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4622 search = malloc(sizeof(struct chanservSearch));
4623 memset(search, 0, sizeof(*search));
4626 for(i = 0; i < argc; i++)
4628 /* Assume all criteria require arguments. */
4631 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4635 if(!irccasecmp(argv[i], "name"))
4636 search->name = argv[++i];
4637 else if(!irccasecmp(argv[i], "registrar"))
4638 search->registrar = argv[++i];
4639 else if(!irccasecmp(argv[i], "unvisited"))
4640 search->unvisited = ParseInterval(argv[++i]);
4641 else if(!irccasecmp(argv[i], "registered"))
4642 search->registered = ParseInterval(argv[++i]);
4643 else if(!irccasecmp(argv[i], "flags"))
4646 if(!irccasecmp(argv[i], "nodelete"))
4647 search->flags |= CHANNEL_NODELETE;
4648 else if(!irccasecmp(argv[i], "suspended"))
4649 search->flags |= CHANNEL_SUSPENDED;
4652 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4656 else if(!irccasecmp(argv[i], "limit"))
4657 search->limit = strtoul(argv[++i], NULL, 10);
4660 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4665 if(search->name && !strcmp(search->name, "*"))
4667 if(search->registrar && !strcmp(search->registrar, "*"))
4668 search->registrar = 0;
4677 chanserv_channel_match(struct chanData *channel, search_t search)
4679 const char *name = channel->channel->name;
4680 if((search->name && !match_ircglob(name, search->name)) ||
4681 (search->registrar && !channel->registrar) ||
4682 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4683 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4684 (search->registered && (now - channel->registered) > search->registered) ||
4685 (search->flags && ((search->flags & channel->flags) != search->flags)))
4692 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4694 struct chanData *channel;
4695 unsigned int matches = 0;
4697 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4699 if(!chanserv_channel_match(channel, search))
4709 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4714 search_print(struct chanData *channel, void *data)
4716 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4719 static CHANSERV_FUNC(cmd_search)
4722 unsigned int matches;
4723 channel_search_func action;
4727 if(!irccasecmp(argv[1], "count"))
4728 action = search_count;
4729 else if(!irccasecmp(argv[1], "print"))
4730 action = search_print;
4733 reply("CSMSG_ACTION_INVALID", argv[1]);
4737 search = chanserv_search_create(user, argc - 2, argv + 2);
4741 if(action == search_count)
4742 search->limit = INT_MAX;
4744 if(action == search_print)
4745 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4747 matches = chanserv_channel_search(search, action, user);
4750 reply("MSG_MATCH_COUNT", matches);
4752 reply("MSG_NO_MATCHES");
4758 static CHANSERV_FUNC(cmd_unvisited)
4760 struct chanData *cData;
4761 time_t interval = chanserv_conf.channel_expire_delay;
4762 char buffer[INTERVALLEN];
4763 unsigned int limit = 25, matches = 0;
4767 interval = ParseInterval(argv[1]);
4769 limit = atoi(argv[2]);
4772 intervalString(buffer, interval, user->handle_info);
4773 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4775 for(cData = channelList; cData && matches < limit; cData = cData->next)
4777 if((now - cData->visited) < interval)
4780 intervalString(buffer, now - cData->visited, user->handle_info);
4781 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4788 static MODCMD_FUNC(chan_opt_defaulttopic)
4794 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4796 reply("CSMSG_TOPIC_LOCKED", channel->name);
4800 topic = unsplit_string(argv+1, argc-1, NULL);
4802 free(channel->channel_info->topic);
4803 if(topic[0] == '*' && topic[1] == 0)
4805 topic = channel->channel_info->topic = NULL;
4809 topic = channel->channel_info->topic = strdup(topic);
4810 if(channel->channel_info->topic_mask
4811 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4812 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4814 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4817 if(channel->channel_info->topic)
4818 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4820 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4824 static MODCMD_FUNC(chan_opt_topicmask)
4828 struct chanData *cData = channel->channel_info;
4831 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4833 reply("CSMSG_TOPIC_LOCKED", channel->name);
4837 mask = unsplit_string(argv+1, argc-1, NULL);
4839 if(cData->topic_mask)
4840 free(cData->topic_mask);
4841 if(mask[0] == '*' && mask[1] == 0)
4843 cData->topic_mask = 0;
4847 cData->topic_mask = strdup(mask);
4849 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4850 else if(!match_ircglob(cData->topic, cData->topic_mask))
4851 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4855 if(channel->channel_info->topic_mask)
4856 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4858 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4862 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4866 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4870 if(greeting[0] == '*' && greeting[1] == 0)
4874 unsigned int length = strlen(greeting);
4875 if(length > chanserv_conf.greeting_length)
4877 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4880 *data = strdup(greeting);
4889 reply(name, user_find_message(user, "MSG_NONE"));
4893 static MODCMD_FUNC(chan_opt_greeting)
4895 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4898 static MODCMD_FUNC(chan_opt_usergreeting)
4900 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4903 static MODCMD_FUNC(chan_opt_modes)
4905 struct mod_chanmode *new_modes;
4906 char modes[MODELEN];
4910 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4912 reply("CSMSG_NO_ACCESS");
4915 if(argv[1][0] == '*' && argv[1][1] == 0)
4917 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4919 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4921 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4924 else if(new_modes->argc > 1)
4926 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4927 mod_chanmode_free(new_modes);
4932 channel->channel_info->modes = *new_modes;
4933 modcmd_chanmode_announce(new_modes);
4934 mod_chanmode_free(new_modes);
4938 mod_chanmode_format(&channel->channel_info->modes, modes);
4940 reply("CSMSG_SET_MODES", modes);
4942 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4946 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4948 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4950 struct chanData *cData = channel->channel_info;
4955 /* Set flag according to value. */
4956 if(enabled_string(argv[1]))
4958 cData->flags |= mask;
4961 else if(disabled_string(argv[1]))
4963 cData->flags &= ~mask;
4968 reply("MSG_INVALID_BINARY", argv[1]);
4974 /* Find current option value. */
4975 value = (cData->flags & mask) ? 1 : 0;
4979 reply(name, user_find_message(user, "MSG_ON"));
4981 reply(name, user_find_message(user, "MSG_OFF"));
4985 static MODCMD_FUNC(chan_opt_nodelete)
4987 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4989 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4993 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4996 static MODCMD_FUNC(chan_opt_dynlimit)
4998 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5001 static MODCMD_FUNC(chan_opt_offchannel)
5003 struct chanData *cData = channel->channel_info;
5008 /* Set flag according to value. */
5009 if(enabled_string(argv[1]))
5011 if(!IsOffChannel(cData))
5012 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5013 cData->flags |= CHANNEL_OFFCHANNEL;
5016 else if(disabled_string(argv[1]))
5018 if(IsOffChannel(cData))
5020 struct mod_chanmode change;
5021 mod_chanmode_init(&change);
5023 change.args[0].mode = MODE_CHANOP;
5024 change.args[0].u.member = AddChannelUser(chanserv, channel);
5025 mod_chanmode_announce(chanserv, channel, &change);
5027 cData->flags &= ~CHANNEL_OFFCHANNEL;
5032 reply("MSG_INVALID_BINARY", argv[1]);
5038 /* Find current option value. */
5039 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5043 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5045 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5049 static MODCMD_FUNC(chan_opt_defaults)
5051 struct userData *uData;
5052 struct chanData *cData;
5053 const char *confirm;
5054 enum levelOption lvlOpt;
5055 enum charOption chOpt;
5057 cData = channel->channel_info;
5058 uData = GetChannelUser(cData, user->handle_info);
5059 if(!uData || (uData->access < UL_OWNER))
5061 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5064 confirm = make_confirmation_string(uData);
5065 if((argc < 2) || strcmp(argv[1], confirm))
5067 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5070 cData->flags = CHANNEL_DEFAULT_FLAGS;
5071 cData->modes = chanserv_conf.default_modes;
5072 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5073 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5074 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5075 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5076 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5081 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5083 struct chanData *cData = channel->channel_info;
5084 struct userData *uData;
5085 unsigned short value;
5089 if(!check_user_level(channel, user, option, 1, 1))
5091 reply("CSMSG_CANNOT_SET");
5094 value = user_level_from_name(argv[1], UL_OWNER+1);
5095 if(!value && strcmp(argv[1], "0"))
5097 reply("CSMSG_INVALID_ACCESS", argv[1]);
5100 uData = GetChannelUser(cData, user->handle_info);
5101 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5103 reply("CSMSG_BAD_SETLEVEL");
5109 if(value > cData->lvlOpts[lvlGiveOps])
5111 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5116 if(value < cData->lvlOpts[lvlGiveVoice])
5118 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5123 /* This test only applies to owners, since non-owners
5124 * trying to set an option to above their level get caught
5125 * by the CSMSG_BAD_SETLEVEL test above.
5127 if(value > uData->access)
5129 reply("CSMSG_BAD_SETTERS");
5136 cData->lvlOpts[option] = value;
5138 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5142 static MODCMD_FUNC(chan_opt_enfops)
5144 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5147 static MODCMD_FUNC(chan_opt_giveops)
5149 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5152 static MODCMD_FUNC(chan_opt_enfmodes)
5154 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5157 static MODCMD_FUNC(chan_opt_enftopic)
5159 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5162 static MODCMD_FUNC(chan_opt_pubcmd)
5164 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5167 static MODCMD_FUNC(chan_opt_setters)
5169 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5172 static MODCMD_FUNC(chan_opt_ctcpusers)
5174 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5177 static MODCMD_FUNC(chan_opt_userinfo)
5179 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5182 static MODCMD_FUNC(chan_opt_givevoice)
5184 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5187 static MODCMD_FUNC(chan_opt_topicsnarf)
5189 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5192 static MODCMD_FUNC(chan_opt_inviteme)
5194 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5198 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5200 struct chanData *cData = channel->channel_info;
5201 int count = charOptions[option].count, index;
5205 index = atoi(argv[1]);
5207 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5209 reply("CSMSG_INVALID_NUMERIC", index);
5210 /* Show possible values. */
5211 for(index = 0; index < count; index++)
5212 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5216 cData->chOpts[option] = charOptions[option].values[index].value;
5220 /* Find current option value. */
5223 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5227 /* Somehow, the option value is corrupt; reset it to the default. */
5228 cData->chOpts[option] = charOptions[option].default_value;
5233 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5237 static MODCMD_FUNC(chan_opt_protect)
5239 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5242 static MODCMD_FUNC(chan_opt_toys)
5244 return channel_multiple_option(chToys, CSFUNC_ARGS);
5247 static MODCMD_FUNC(chan_opt_ctcpreaction)
5249 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5252 static MODCMD_FUNC(chan_opt_topicrefresh)
5254 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5257 static struct svccmd_list set_shows_list;
5260 handle_svccmd_unbind(struct svccmd *target) {
5262 for(ii=0; ii<set_shows_list.used; ++ii)
5263 if(target == set_shows_list.list[ii])
5264 set_shows_list.used = 0;
5267 static CHANSERV_FUNC(cmd_set)
5269 struct svccmd *subcmd;
5273 /* Check if we need to (re-)initialize set_shows_list. */
5274 if(!set_shows_list.used)
5276 if(!set_shows_list.size)
5278 set_shows_list.size = chanserv_conf.set_shows->used;
5279 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5281 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5283 const char *name = chanserv_conf.set_shows->list[ii];
5284 sprintf(buf, "%s %s", argv[0], name);
5285 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5288 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5291 svccmd_list_append(&set_shows_list, subcmd);
5297 reply("CSMSG_CHANNEL_OPTIONS");
5298 for(ii = 0; ii < set_shows_list.used; ii++)
5300 subcmd = set_shows_list.list[ii];
5301 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5306 sprintf(buf, "%s %s", argv[0], argv[1]);
5307 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5310 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5313 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5315 reply("CSMSG_NO_ACCESS");
5319 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5323 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5325 struct userData *uData;
5327 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5330 reply("CSMSG_NOT_USER", channel->name);
5336 /* Just show current option value. */
5338 else if(enabled_string(argv[1]))
5340 uData->flags |= mask;
5342 else if(disabled_string(argv[1]))
5344 uData->flags &= ~mask;
5348 reply("MSG_INVALID_BINARY", argv[1]);
5352 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5356 static MODCMD_FUNC(user_opt_noautoop)
5358 struct userData *uData;
5360 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5363 reply("CSMSG_NOT_USER", channel->name);
5366 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5367 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5369 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5372 static MODCMD_FUNC(user_opt_autoinvite)
5374 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5377 static MODCMD_FUNC(user_opt_info)
5379 struct userData *uData;
5382 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5386 /* If they got past the command restrictions (which require access)
5387 * but fail this test, we have some fool with security override on.
5389 reply("CSMSG_NOT_USER", channel->name);
5396 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5397 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5399 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5402 bp = strcspn(infoline, "\001");
5405 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5410 if(infoline[0] == '*' && infoline[1] == 0)
5413 uData->info = strdup(infoline);
5416 reply("CSMSG_USET_INFO", uData->info);
5418 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5422 struct svccmd_list uset_shows_list;
5424 static CHANSERV_FUNC(cmd_uset)
5426 struct svccmd *subcmd;
5430 /* Check if we need to (re-)initialize uset_shows_list. */
5431 if(!uset_shows_list.used)
5435 "NoAutoOp", "AutoInvite", "Info"
5438 if(!uset_shows_list.size)
5440 uset_shows_list.size = ArrayLength(options);
5441 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5443 for(ii = 0; ii < ArrayLength(options); ii++)
5445 const char *name = options[ii];
5446 sprintf(buf, "%s %s", argv[0], name);
5447 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5450 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5453 svccmd_list_append(&uset_shows_list, subcmd);
5459 /* Do this so options are presented in a consistent order. */
5460 reply("CSMSG_USER_OPTIONS");
5461 for(ii = 0; ii < uset_shows_list.used; ii++)
5462 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5466 sprintf(buf, "%s %s", argv[0], argv[1]);
5467 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5470 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5474 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5477 static CHANSERV_FUNC(cmd_giveownership)
5479 struct handle_info *new_owner_hi;
5480 struct userData *new_owner, *curr_user;
5481 struct chanData *cData = channel->channel_info;
5482 struct do_not_register *dnr;
5484 unsigned short co_access;
5485 char reason[MAXLEN];
5488 curr_user = GetChannelAccess(cData, user->handle_info);
5489 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5490 if(!curr_user || (curr_user->access != UL_OWNER))
5492 struct userData *owner = NULL;
5493 for(curr_user = channel->channel_info->users;
5495 curr_user = curr_user->next)
5497 if(curr_user->access != UL_OWNER)
5501 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5508 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5510 char delay[INTERVALLEN];
5511 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5512 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5515 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5517 if(new_owner_hi == user->handle_info)
5519 reply("CSMSG_NO_TRANSFER_SELF");
5522 new_owner = GetChannelAccess(cData, new_owner_hi);
5527 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5531 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5535 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5537 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5540 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5541 if(!IsHelping(user))
5542 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5544 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5547 if(new_owner->access >= UL_COOWNER)
5548 co_access = new_owner->access;
5550 co_access = UL_COOWNER;
5551 new_owner->access = UL_OWNER;
5553 curr_user->access = co_access;
5554 cData->ownerTransfer = now;
5555 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5556 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5557 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5561 static CHANSERV_FUNC(cmd_suspend)
5563 struct handle_info *hi;
5564 struct userData *self, *target;
5567 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5568 self = GetChannelUser(channel->channel_info, user->handle_info);
5569 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5571 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5574 if(target->access >= self->access)
5576 reply("MSG_USER_OUTRANKED", hi->handle);
5579 if(target->flags & USER_SUSPENDED)
5581 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5586 target->present = 0;
5589 target->flags |= USER_SUSPENDED;
5590 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5594 static CHANSERV_FUNC(cmd_unsuspend)
5596 struct handle_info *hi;
5597 struct userData *self, *target;
5600 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5601 self = GetChannelUser(channel->channel_info, user->handle_info);
5602 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5604 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5607 if(target->access >= self->access)
5609 reply("MSG_USER_OUTRANKED", hi->handle);
5612 if(!(target->flags & USER_SUSPENDED))
5614 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5617 target->flags &= ~USER_SUSPENDED;
5618 scan_user_presence(target, NULL);
5619 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5623 static MODCMD_FUNC(cmd_deleteme)
5625 struct handle_info *hi;
5626 struct userData *target;
5627 const char *confirm_string;
5628 unsigned short access;
5631 hi = user->handle_info;
5632 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5634 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5637 if(target->access == UL_OWNER)
5639 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5642 confirm_string = make_confirmation_string(target);
5643 if((argc < 2) || strcmp(argv[1], confirm_string))
5645 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5648 access = target->access;
5649 channel_name = strdup(channel->name);
5650 del_channel_user(target, 1);
5651 reply("CSMSG_DELETED_YOU", access, channel_name);
5657 chanserv_refresh_topics(UNUSED_ARG(void *data))
5659 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5660 struct chanData *cData;
5663 for(cData = channelList; cData; cData = cData->next)
5665 if(IsSuspended(cData))
5667 opt = cData->chOpts[chTopicRefresh];
5670 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5673 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5674 cData->last_refresh = refresh_num;
5676 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5679 static CHANSERV_FUNC(cmd_unf)
5683 char response[MAXLEN];
5684 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5685 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5686 irc_privmsg(cmd->parent->bot, channel->name, response);
5689 reply("CSMSG_UNF_RESPONSE");
5693 static CHANSERV_FUNC(cmd_ping)
5697 char response[MAXLEN];
5698 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5699 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5700 irc_privmsg(cmd->parent->bot, channel->name, response);
5703 reply("CSMSG_PING_RESPONSE");
5707 static CHANSERV_FUNC(cmd_wut)
5711 char response[MAXLEN];
5712 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5713 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5714 irc_privmsg(cmd->parent->bot, channel->name, response);
5717 reply("CSMSG_WUT_RESPONSE");
5721 static CHANSERV_FUNC(cmd_8ball)
5723 unsigned int i, j, accum;
5728 for(i=1; i<argc; i++)
5729 for(j=0; argv[i][j]; j++)
5730 accum = (accum << 5) - accum + toupper(argv[i][j]);
5731 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5734 char response[MAXLEN];
5735 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5736 irc_privmsg(cmd->parent->bot, channel->name, response);
5739 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5743 static CHANSERV_FUNC(cmd_d)
5745 unsigned long sides, count, modifier, ii, total;
5746 char response[MAXLEN], *sep;
5750 if((count = strtoul(argv[1], &sep, 10)) < 1)
5760 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5761 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5765 else if((sep[0] == '-') && isdigit(sep[1]))
5766 modifier = strtoul(sep, NULL, 10);
5767 else if((sep[0] == '+') && isdigit(sep[1]))
5768 modifier = strtoul(sep+1, NULL, 10);
5775 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5780 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5783 for(total = ii = 0; ii < count; ++ii)
5784 total += (rand() % sides) + 1;
5787 if((count > 1) || modifier)
5789 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5790 sprintf(response, fmt, total, count, sides, modifier);
5794 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5795 sprintf(response, fmt, total, sides);
5798 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5800 send_message_type(4, user, cmd->parent->bot, "%s", response);
5804 static CHANSERV_FUNC(cmd_huggle)
5806 /* CTCP must be via PRIVMSG, never notice */
5808 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5810 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5815 chanserv_adjust_limit(void *data)
5817 struct mod_chanmode change;
5818 struct chanData *cData = data;
5819 struct chanNode *channel = cData->channel;
5822 if(IsSuspended(cData))
5825 cData->limitAdjusted = now;
5826 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5827 if(cData->modes.modes_set & MODE_LIMIT)
5829 if(limit > cData->modes.new_limit)
5830 limit = cData->modes.new_limit;
5831 else if(limit == cData->modes.new_limit)
5835 mod_chanmode_init(&change);
5836 change.modes_set = MODE_LIMIT;
5837 change.new_limit = limit;
5838 mod_chanmode_announce(chanserv, channel, &change);
5842 handle_new_channel(struct chanNode *channel)
5844 struct chanData *cData;
5846 if(!(cData = channel->channel_info))
5849 if(cData->modes.modes_set || cData->modes.modes_clear)
5850 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5852 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5853 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5856 /* Welcome to my worst nightmare. Warning: Read (or modify)
5857 the code below at your own risk. */
5859 handle_join(struct modeNode *mNode)
5861 struct mod_chanmode change;
5862 struct userNode *user = mNode->user;
5863 struct chanNode *channel = mNode->channel;
5864 struct chanData *cData;
5865 struct userData *uData = NULL;
5866 struct banData *bData;
5867 struct handle_info *handle;
5868 unsigned int modes = 0, info = 0;
5871 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5874 cData = channel->channel_info;
5875 if(channel->members.used > cData->max)
5876 cData->max = channel->members.used;
5878 /* Check for bans. If they're joining through a ban, one of two
5880 * 1: Join during a netburst, by riding the break. Kick them
5881 * unless they have ops or voice in the channel.
5882 * 2: They're allowed to join through the ban (an invite in
5883 * ircu2.10, or a +e on Hybrid, or something).
5884 * If they're not joining through a ban, and the banlist is not
5885 * full, see if they're on the banlist for the channel. If so,
5888 if(user->uplink->burst && !mNode->modes)
5891 for(ii = 0; ii < channel->banlist.used; ii++)
5893 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK | MATCH_VISIBLE))
5895 /* Riding a netburst. Naughty. */
5896 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5902 mod_chanmode_init(&change);
5904 if(channel->banlist.used < MAXBANS)
5906 /* Not joining through a ban. */
5907 for(bData = cData->bans;
5908 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE);
5909 bData = bData->next);
5913 char kick_reason[MAXLEN];
5914 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5916 bData->triggered = now;
5917 if(bData != cData->bans)
5919 /* Shuffle the ban to the head of the list. */
5921 bData->next->prev = bData->prev;
5923 bData->prev->next = bData->next;
5926 bData->next = cData->bans;
5929 cData->bans->prev = bData;
5930 cData->bans = bData;
5933 change.args[0].mode = MODE_BAN;
5934 change.args[0].u.hostmask = bData->mask;
5935 mod_chanmode_announce(chanserv, channel, &change);
5936 KickChannelUser(user, channel, chanserv, kick_reason);
5941 /* ChanServ will not modify the limits in join-flooded channels.
5942 It will also skip DynLimit processing when the user (or srvx)
5943 is bursting in, because there are likely more incoming. */
5944 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5945 && !user->uplink->burst
5946 && !channel->join_flooded
5947 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5949 /* The user count has begun "bumping" into the channel limit,
5950 so set a timer to raise the limit a bit. Any previous
5951 timers are removed so three incoming users within the delay
5952 results in one limit change, not three. */
5954 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5955 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5958 if(channel->join_flooded)
5960 /* don't automatically give ops or voice during a join flood */
5962 else if(cData->lvlOpts[lvlGiveOps] == 0)
5963 modes |= MODE_CHANOP;
5964 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5965 modes |= MODE_VOICE;
5967 greeting = cData->greeting;
5968 if(user->handle_info)
5970 handle = user->handle_info;
5972 if(IsHelper(user) && !IsHelping(user))
5975 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5977 if(channel == chanserv_conf.support_channels.list[ii])
5979 HANDLE_SET_FLAG(user->handle_info, HELPING);
5985 uData = GetTrueChannelAccess(cData, handle);
5986 if(uData && !IsUserSuspended(uData))
5988 /* Ops and above were handled by the above case. */
5989 if(IsUserAutoOp(uData))
5991 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5992 modes |= MODE_CHANOP;
5993 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5994 modes |= MODE_VOICE;
5996 if(uData->access >= UL_PRESENT)
5997 cData->visited = now;
5998 if(cData->user_greeting)
5999 greeting = cData->user_greeting;
6001 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6002 && ((now - uData->seen) >= chanserv_conf.info_delay)
6009 if(!user->uplink->burst)
6013 if(modes & MODE_CHANOP)
6014 modes &= ~MODE_VOICE;
6015 change.args[0].mode = modes;
6016 change.args[0].u.member = mNode;
6017 mod_chanmode_announce(chanserv, channel, &change);
6019 if(greeting && !user->uplink->burst)
6020 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6022 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6028 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6030 struct mod_chanmode change;
6031 struct userData *channel;
6032 unsigned int ii, jj;
6034 if(!user->handle_info)
6037 mod_chanmode_init(&change);
6039 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6041 struct chanNode *cn;
6042 struct modeNode *mn;
6043 if(IsUserSuspended(channel)
6044 || IsSuspended(channel->channel)
6045 || !(cn = channel->channel->channel))
6048 mn = GetUserMode(cn, user);
6051 if(!IsUserSuspended(channel)
6052 && IsUserAutoInvite(channel)
6053 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6055 && !user->uplink->burst)
6056 irc_invite(chanserv, user, cn);
6060 if(channel->access >= UL_PRESENT)
6061 channel->channel->visited = now;
6063 if(IsUserAutoOp(channel))
6065 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6066 change.args[0].mode = MODE_CHANOP;
6067 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6068 change.args[0].mode = MODE_VOICE;
6070 change.args[0].mode = 0;
6071 change.args[0].u.member = mn;
6072 if(change.args[0].mode)
6073 mod_chanmode_announce(chanserv, cn, &change);
6076 channel->seen = now;
6077 channel->present = 1;
6080 for(ii = 0; ii < user->channels.used; ++ii)
6082 struct chanNode *channel = user->channels.list[ii]->channel;
6083 struct banData *ban;
6085 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6086 || !channel->channel_info
6087 || IsSuspended(channel->channel_info))
6089 for(jj = 0; jj < channel->banlist.used; ++jj)
6090 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK | MATCH_VISIBLE))
6092 if(jj < channel->banlist.used)
6094 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6096 char kick_reason[MAXLEN];
6097 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6099 change.args[0].mode = MODE_BAN;
6100 change.args[0].u.hostmask = ban->mask;
6101 mod_chanmode_announce(chanserv, channel, &change);
6102 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6103 KickChannelUser(user, channel, chanserv, kick_reason);
6104 ban->triggered = now;
6109 if(IsSupportHelper(user))
6111 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6113 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6115 HANDLE_SET_FLAG(user->handle_info, HELPING);
6123 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6125 struct chanData *cData;
6126 struct userData *uData;
6128 cData = mn->channel->channel_info;
6129 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6132 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6134 /* Allow for a bit of padding so that the limit doesn't
6135 track the user count exactly, which could get annoying. */
6136 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6138 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6139 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6143 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6145 scan_user_presence(uData, mn->user);
6149 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6151 unsigned int ii, jj;
6152 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6154 for(jj = 0; jj < mn->user->channels.used; ++jj)
6155 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6157 if(jj < mn->user->channels.used)
6160 if(ii == chanserv_conf.support_channels.used)
6161 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6166 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6168 struct userData *uData;
6170 if(!channel->channel_info || !kicker || IsService(kicker)
6171 || (kicker == victim) || IsSuspended(channel->channel_info)
6172 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6175 if(protect_user(victim, kicker, channel->channel_info))
6177 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6178 KickChannelUser(kicker, channel, chanserv, reason);
6181 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6186 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6188 struct chanData *cData;
6190 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6193 cData = channel->channel_info;
6194 if(bad_topic(channel, user, channel->topic))
6196 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6197 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6198 SetChannelTopic(channel, chanserv, old_topic, 1);
6199 else if(cData->topic)
6200 SetChannelTopic(channel, chanserv, cData->topic, 1);
6203 /* With topicsnarf, grab the topic and save it as the default topic. */
6204 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6207 cData->topic = strdup(channel->topic);
6213 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6215 struct mod_chanmode *bounce = NULL;
6216 unsigned int bnc, ii;
6219 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6222 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6223 && mode_lock_violated(&channel->channel_info->modes, change))
6225 char correct[MAXLEN];
6226 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6227 mod_chanmode_format(&channel->channel_info->modes, correct);
6228 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6230 for(ii = bnc = 0; ii < change->argc; ++ii)
6232 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6234 const struct userNode *victim = change->args[ii].u.member->user;
6235 if(!protect_user(victim, user, channel->channel_info))
6238 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6241 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6242 bounce->args[bnc].u.member = GetUserMode(channel, user);
6243 if(bounce->args[bnc].u.member)
6247 bounce->args[bnc].mode = MODE_CHANOP;
6248 bounce->args[bnc].u.member = change->args[ii].u.member;
6250 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6252 else if(change->args[ii].mode & MODE_CHANOP)
6254 const struct userNode *victim = change->args[ii].u.member->user;
6255 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6258 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6259 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6260 bounce->args[bnc].u.member = change->args[ii].u.member;
6263 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6265 const char *ban = change->args[ii].u.hostmask;
6266 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6269 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6270 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6271 bounce->args[bnc].u.hostmask = strdup(ban);
6273 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6278 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6279 mod_chanmode_announce(chanserv, channel, bounce);
6280 for(ii = 0; ii < change->argc; ++ii)
6281 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6282 free((char*)bounce->args[ii].u.hostmask);
6283 mod_chanmode_free(bounce);
6288 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6290 struct chanNode *channel;
6291 struct banData *bData;
6292 struct mod_chanmode change;
6293 unsigned int ii, jj;
6294 char kick_reason[MAXLEN];
6296 mod_chanmode_init(&change);
6298 change.args[0].mode = MODE_BAN;
6299 for(ii = 0; ii < user->channels.used; ++ii)
6301 channel = user->channels.list[ii]->channel;
6302 /* Need not check for bans if they're opped or voiced. */
6303 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6305 /* Need not check for bans unless channel registration is active. */
6306 if(!channel->channel_info || IsSuspended(channel->channel_info))
6308 /* Look for a matching ban already on the channel. */
6309 for(jj = 0; jj < channel->banlist.used; ++jj)
6310 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK | MATCH_VISIBLE))
6312 /* Need not act if we found one. */
6313 if(jj < channel->banlist.used)
6315 /* Look for a matching ban in this channel. */
6316 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6318 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6320 change.args[0].u.hostmask = bData->mask;
6321 mod_chanmode_announce(chanserv, channel, &change);
6322 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6323 KickChannelUser(user, channel, chanserv, kick_reason);
6324 bData->triggered = now;
6325 break; /* we don't need to check any more bans in the channel */
6330 static void handle_rename(struct handle_info *handle, const char *old_handle)
6332 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6336 dict_remove2(handle_dnrs, old_handle, 1);
6337 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6338 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6343 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6345 struct userNode *h_user;
6347 if(handle->channels)
6349 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6350 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6352 while(handle->channels)
6353 del_channel_user(handle->channels, 1);
6358 handle_server_link(UNUSED_ARG(struct server *server))
6360 struct chanData *cData;
6362 for(cData = channelList; cData; cData = cData->next)
6364 if(!IsSuspended(cData))
6365 cData->may_opchan = 1;
6366 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6367 && !cData->channel->join_flooded
6368 && ((cData->channel->limit - cData->channel->members.used)
6369 < chanserv_conf.adjust_threshold))
6371 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6372 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6378 chanserv_conf_read(void)
6382 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6383 struct mod_chanmode *change;
6384 struct string_list *strlist;
6385 struct chanNode *chan;
6388 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6390 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6393 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6394 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6395 chanserv_conf.support_channels.used = 0;
6396 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6398 for(ii = 0; ii < strlist->used; ++ii)
6400 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6403 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6405 channelList_append(&chanserv_conf.support_channels, chan);
6408 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6411 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6414 chan = AddChannel(str, now, str2, NULL);
6416 channelList_append(&chanserv_conf.support_channels, chan);
6418 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6419 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6420 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6421 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6422 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6423 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6424 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6425 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6426 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6427 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6428 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6429 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6430 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6431 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6432 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6433 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6434 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6435 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6436 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6437 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6438 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6439 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6440 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6442 NickChange(chanserv, str, 0);
6443 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6444 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6445 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6446 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6447 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6448 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6449 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6450 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6451 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6452 chanserv_conf.max_owned = str ? atoi(str) : 5;
6453 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6454 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6455 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6456 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6457 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6458 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6459 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6462 safestrncpy(mode_line, str, sizeof(mode_line));
6463 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6464 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6465 && (change->argc < 2))
6467 chanserv_conf.default_modes = *change;
6468 mod_chanmode_free(change);
6470 free_string_list(chanserv_conf.set_shows);
6471 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6473 strlist = string_list_copy(strlist);
6476 static const char *list[] = {
6477 /* free form text */
6478 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6479 /* options based on user level */
6480 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6481 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6482 /* multiple choice options */
6483 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6484 /* binary options */
6485 "DynLimit", "NoDelete",
6490 strlist = alloc_string_list(ArrayLength(list)-1);
6491 for(ii=0; list[ii]; ii++)
6492 string_list_append(strlist, strdup(list[ii]));
6494 chanserv_conf.set_shows = strlist;
6495 /* We don't look things up now, in case the list refers to options
6496 * defined by modules initialized after this point. Just mark the
6497 * function list as invalid, so it will be initialized.
6499 set_shows_list.used = 0;
6500 free_string_list(chanserv_conf.eightball);
6501 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6504 strlist = string_list_copy(strlist);
6508 strlist = alloc_string_list(4);
6509 string_list_append(strlist, strdup("Yes."));
6510 string_list_append(strlist, strdup("No."));
6511 string_list_append(strlist, strdup("Maybe so."));
6513 chanserv_conf.eightball = strlist;
6514 free_string_list(chanserv_conf.old_ban_names);
6515 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6517 strlist = string_list_copy(strlist);
6519 strlist = alloc_string_list(2);
6520 chanserv_conf.old_ban_names = strlist;
6521 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6522 off_channel = str ? atoi(str) : 0;
6526 chanserv_note_type_read(const char *key, struct record_data *rd)
6529 struct note_type *ntype;
6532 if(!(obj = GET_RECORD_OBJECT(rd)))
6534 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6537 if(!(ntype = chanserv_create_note_type(key)))
6539 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6543 /* Figure out set access */
6544 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6546 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6547 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6549 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6551 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6552 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6554 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6556 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6560 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6561 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6562 ntype->set_access.min_opserv = 0;
6565 /* Figure out visibility */
6566 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6567 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6568 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6569 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6570 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6571 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6572 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6573 ntype->visible_type = NOTE_VIS_ALL;
6575 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6577 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6578 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6582 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6584 struct handle_info *handle;
6585 struct userData *uData;
6586 char *seen, *inf, *flags;
6588 unsigned short access;
6590 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6592 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6596 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6597 if(access > UL_OWNER)
6599 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6603 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6604 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6605 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6606 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6607 handle = get_handle_info(key);
6610 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6614 uData = add_channel_user(chan, handle, access, last_seen, inf);
6615 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6619 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6621 struct banData *bData;
6622 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6623 time_t set_time, triggered_time, expires_time;
6625 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6627 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6631 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6632 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6633 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6634 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6635 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6636 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6637 if (!reason || !owner)
6640 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6641 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6643 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6645 expires_time = set_time + atoi(s_duration);
6649 if(!reason || (expires_time && (expires_time < now)))
6652 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6655 static struct suspended *
6656 chanserv_read_suspended(dict_t obj)
6658 struct suspended *suspended = calloc(1, sizeof(*suspended));
6662 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6663 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6664 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6665 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6666 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6667 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6668 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6669 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6670 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6671 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6676 chanserv_channel_read(const char *key, struct record_data *hir)
6678 struct suspended *suspended;
6679 struct mod_chanmode *modes;
6680 struct chanNode *cNode;
6681 struct chanData *cData;
6682 struct dict *channel, *obj;
6683 char *str, *argv[10];
6687 channel = hir->d.object;
6689 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6692 cNode = AddChannel(key, now, NULL, NULL);
6695 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6698 cData = register_channel(cNode, str);
6701 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6705 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6707 enum levelOption lvlOpt;
6708 enum charOption chOpt;
6710 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6711 cData->flags = atoi(str);
6713 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6715 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6717 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6718 else if(levelOptions[lvlOpt].old_flag)
6720 if(cData->flags & levelOptions[lvlOpt].old_flag)
6721 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6723 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6727 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6729 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6731 cData->chOpts[chOpt] = str[0];
6734 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6736 enum levelOption lvlOpt;
6737 enum charOption chOpt;
6740 cData->flags = base64toint(str, 5);
6741 count = strlen(str += 5);
6742 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6745 if(levelOptions[lvlOpt].old_flag)
6747 if(cData->flags & levelOptions[lvlOpt].old_flag)
6748 lvl = levelOptions[lvlOpt].flag_value;
6750 lvl = levelOptions[lvlOpt].default_value;
6752 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6754 case 'c': lvl = UL_COOWNER; break;
6755 case 'm': lvl = UL_MASTER; break;
6756 case 'n': lvl = UL_OWNER+1; break;
6757 case 'o': lvl = UL_OP; break;
6758 case 'p': lvl = UL_PEON; break;
6759 case 'w': lvl = UL_OWNER; break;
6760 default: lvl = 0; break;
6762 cData->lvlOpts[lvlOpt] = lvl;
6764 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6765 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6768 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6770 suspended = chanserv_read_suspended(obj);
6771 cData->suspended = suspended;
6772 suspended->cData = cData;
6773 /* We could use suspended->expires and suspended->revoked to
6774 * set the CHANNEL_SUSPENDED flag, but we don't. */
6776 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6778 suspended = calloc(1, sizeof(*suspended));
6779 suspended->issued = 0;
6780 suspended->revoked = 0;
6781 suspended->suspender = strdup(str);
6782 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6783 suspended->expires = str ? atoi(str) : 0;
6784 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6785 suspended->reason = strdup(str ? str : "No reason");
6786 suspended->previous = NULL;
6787 cData->suspended = suspended;
6788 suspended->cData = cData;
6792 cData->flags &= ~CHANNEL_SUSPENDED;
6793 suspended = NULL; /* to squelch a warning */
6796 if(IsSuspended(cData)) {
6797 if(suspended->expires > now)
6798 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6799 else if(suspended->expires)
6800 cData->flags &= ~CHANNEL_SUSPENDED;
6803 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6804 struct mod_chanmode change;
6805 mod_chanmode_init(&change);
6807 change.args[0].mode = MODE_CHANOP;
6808 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6809 mod_chanmode_announce(chanserv, cNode, &change);
6812 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6813 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6814 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6815 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6816 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6817 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6818 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6819 cData->max = str ? atoi(str) : 0;
6820 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6821 cData->greeting = str ? strdup(str) : NULL;
6822 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6823 cData->user_greeting = str ? strdup(str) : NULL;
6824 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6825 cData->topic_mask = str ? strdup(str) : NULL;
6826 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6827 cData->topic = str ? strdup(str) : NULL;
6829 if(!IsSuspended(cData)
6830 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6831 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6832 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6833 cData->modes = *modes;
6835 cData->modes.modes_set |= MODE_REGISTERED;
6836 if(cData->modes.argc > 1)
6837 cData->modes.argc = 1;
6838 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6839 mod_chanmode_free(modes);
6842 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6843 for(it = dict_first(obj); it; it = iter_next(it))
6844 user_read_helper(iter_key(it), iter_data(it), cData);
6846 if(!cData->users && !IsProtected(cData))
6848 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6849 unregister_channel(cData, "has empty user list.");
6853 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6854 for(it = dict_first(obj); it; it = iter_next(it))
6855 ban_read_helper(iter_key(it), iter_data(it), cData);
6857 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6858 for(it = dict_first(obj); it; it = iter_next(it))
6860 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6861 struct record_data *rd = iter_data(it);
6862 const char *note, *setter;
6864 if(rd->type != RECDB_OBJECT)
6866 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6870 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6872 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6874 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6878 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6879 if(!setter) setter = "<unknown>";
6880 chanserv_add_channel_note(cData, ntype, setter, note);
6888 chanserv_dnr_read(const char *key, struct record_data *hir)
6890 const char *setter, *reason, *str;
6891 struct do_not_register *dnr;
6893 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6896 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6899 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6902 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6905 dnr = chanserv_add_dnr(key, setter, reason);
6908 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6910 dnr->set = atoi(str);
6916 chanserv_saxdb_read(struct dict *database)
6918 struct dict *section;
6921 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6922 for(it = dict_first(section); it; it = iter_next(it))
6923 chanserv_note_type_read(iter_key(it), iter_data(it));
6925 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6926 for(it = dict_first(section); it; it = iter_next(it))
6927 chanserv_channel_read(iter_key(it), iter_data(it));
6929 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6930 for(it = dict_first(section); it; it = iter_next(it))
6931 chanserv_dnr_read(iter_key(it), iter_data(it));
6937 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6939 int high_present = 0;
6940 saxdb_start_record(ctx, KEY_USERS, 1);
6941 for(; uData; uData = uData->next)
6943 if((uData->access >= UL_PRESENT) && uData->present)
6945 saxdb_start_record(ctx, uData->handle->handle, 0);
6946 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6947 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6949 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6951 saxdb_write_string(ctx, KEY_INFO, uData->info);
6952 saxdb_end_record(ctx);
6954 saxdb_end_record(ctx);
6955 return high_present;
6959 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6963 saxdb_start_record(ctx, KEY_BANS, 1);
6964 for(; bData; bData = bData->next)
6966 saxdb_start_record(ctx, bData->mask, 0);
6967 saxdb_write_int(ctx, KEY_SET, bData->set);
6968 if(bData->triggered)
6969 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6971 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6973 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6975 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6976 saxdb_end_record(ctx);
6978 saxdb_end_record(ctx);
6982 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6984 saxdb_start_record(ctx, name, 0);
6985 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6986 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6988 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6990 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6992 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6994 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6995 saxdb_end_record(ctx);
6999 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7003 enum levelOption lvlOpt;
7004 enum charOption chOpt;
7006 saxdb_start_record(ctx, channel->channel->name, 1);
7008 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7009 saxdb_write_int(ctx, KEY_MAX, channel->max);
7011 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7012 if(channel->registrar)
7013 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7014 if(channel->greeting)
7015 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7016 if(channel->user_greeting)
7017 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7018 if(channel->topic_mask)
7019 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7020 if(channel->suspended)
7021 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7023 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7024 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7025 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7026 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7027 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7029 buf[0] = channel->chOpts[chOpt];
7031 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7033 saxdb_end_record(ctx);
7035 if(channel->modes.modes_set || channel->modes.modes_clear)
7037 mod_chanmode_format(&channel->modes, buf);
7038 saxdb_write_string(ctx, KEY_MODES, buf);
7041 high_present = chanserv_write_users(ctx, channel->users);
7042 chanserv_write_bans(ctx, channel->bans);
7044 if(dict_size(channel->notes))
7048 saxdb_start_record(ctx, KEY_NOTES, 1);
7049 for(it = dict_first(channel->notes); it; it = iter_next(it))
7051 struct note *note = iter_data(it);
7052 saxdb_start_record(ctx, iter_key(it), 0);
7053 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7054 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7055 saxdb_end_record(ctx);
7057 saxdb_end_record(ctx);
7060 if(channel->ownerTransfer)
7061 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7062 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7063 saxdb_end_record(ctx);
7067 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7071 saxdb_start_record(ctx, ntype->name, 0);
7072 switch(ntype->set_access_type)
7074 case NOTE_SET_CHANNEL_ACCESS:
7075 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7077 case NOTE_SET_CHANNEL_SETTER:
7078 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7080 case NOTE_SET_PRIVILEGED: default:
7081 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7084 switch(ntype->visible_type)
7086 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7087 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7088 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7090 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7091 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7092 saxdb_end_record(ctx);
7096 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7098 struct do_not_register *dnr;
7101 for(it = dict_first(dnrs); it; it = iter_next(it))
7103 dnr = iter_data(it);
7104 saxdb_start_record(ctx, dnr->chan_name, 0);
7106 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7107 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7108 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7109 saxdb_end_record(ctx);
7114 chanserv_saxdb_write(struct saxdb_context *ctx)
7117 struct chanData *channel;
7120 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7121 for(it = dict_first(note_types); it; it = iter_next(it))
7122 chanserv_write_note_type(ctx, iter_data(it));
7123 saxdb_end_record(ctx);
7126 saxdb_start_record(ctx, KEY_DNR, 1);
7127 write_dnrs_helper(ctx, handle_dnrs);
7128 write_dnrs_helper(ctx, plain_dnrs);
7129 write_dnrs_helper(ctx, mask_dnrs);
7130 saxdb_end_record(ctx);
7133 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7134 for(channel = channelList; channel; channel = channel->next)
7135 chanserv_write_channel(ctx, channel);
7136 saxdb_end_record(ctx);
7142 chanserv_db_cleanup(void) {
7144 unreg_part_func(handle_part);
7146 unregister_channel(channelList, "terminating.");
7147 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7148 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7149 free(chanserv_conf.support_channels.list);
7150 dict_delete(handle_dnrs);
7151 dict_delete(plain_dnrs);
7152 dict_delete(mask_dnrs);
7153 dict_delete(note_types);
7154 free_string_list(chanserv_conf.eightball);
7155 free_string_list(chanserv_conf.old_ban_names);
7156 free_string_list(chanserv_conf.set_shows);
7157 free(set_shows_list.list);
7158 free(uset_shows_list.list);
7161 struct userData *helper = helperList;
7162 helperList = helperList->next;
7167 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7168 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7169 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7172 init_chanserv(const char *nick)
7174 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7175 conf_register_reload(chanserv_conf_read);
7177 reg_server_link_func(handle_server_link);
7179 reg_new_channel_func(handle_new_channel);
7180 reg_join_func(handle_join);
7181 reg_part_func(handle_part);
7182 reg_kick_func(handle_kick);
7183 reg_topic_func(handle_topic);
7184 reg_mode_change_func(handle_mode);
7185 reg_nick_change_func(handle_nick_change);
7187 reg_auth_func(handle_auth);
7188 reg_handle_rename_func(handle_rename);
7189 reg_unreg_func(handle_unreg);
7191 handle_dnrs = dict_new();
7192 dict_set_free_data(handle_dnrs, free);
7193 plain_dnrs = dict_new();
7194 dict_set_free_data(plain_dnrs, free);
7195 mask_dnrs = dict_new();
7196 dict_set_free_data(mask_dnrs, free);
7198 reg_svccmd_unbind_func(handle_svccmd_unbind);
7199 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7200 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7201 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7202 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7203 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7204 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7205 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7206 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7207 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7209 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7210 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7212 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7213 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7214 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7215 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7216 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7218 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7219 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7220 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7221 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7222 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7224 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7225 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7226 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7227 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7229 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7230 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7231 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7232 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7233 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7234 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7235 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7236 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7238 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7239 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7240 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7241 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7242 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7243 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7244 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7245 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7246 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7247 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7248 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7249 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7250 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7251 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7253 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7254 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7255 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7256 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7257 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7259 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7260 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7262 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7263 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7264 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7265 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7266 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7267 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7268 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7269 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7270 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7271 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7272 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7274 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7275 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7277 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7278 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7279 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7280 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7282 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7283 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7284 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7285 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7286 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7288 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7289 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7290 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7291 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7292 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7293 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7295 /* Channel options */
7296 DEFINE_CHANNEL_OPTION(defaulttopic);
7297 DEFINE_CHANNEL_OPTION(topicmask);
7298 DEFINE_CHANNEL_OPTION(greeting);
7299 DEFINE_CHANNEL_OPTION(usergreeting);
7300 DEFINE_CHANNEL_OPTION(modes);
7301 DEFINE_CHANNEL_OPTION(enfops);
7302 DEFINE_CHANNEL_OPTION(giveops);
7303 DEFINE_CHANNEL_OPTION(protect);
7304 DEFINE_CHANNEL_OPTION(enfmodes);
7305 DEFINE_CHANNEL_OPTION(enftopic);
7306 DEFINE_CHANNEL_OPTION(pubcmd);
7307 DEFINE_CHANNEL_OPTION(givevoice);
7308 DEFINE_CHANNEL_OPTION(userinfo);
7309 DEFINE_CHANNEL_OPTION(dynlimit);
7310 DEFINE_CHANNEL_OPTION(topicsnarf);
7311 DEFINE_CHANNEL_OPTION(nodelete);
7312 DEFINE_CHANNEL_OPTION(toys);
7313 DEFINE_CHANNEL_OPTION(setters);
7314 DEFINE_CHANNEL_OPTION(topicrefresh);
7315 DEFINE_CHANNEL_OPTION(ctcpusers);
7316 DEFINE_CHANNEL_OPTION(ctcpreaction);
7317 DEFINE_CHANNEL_OPTION(inviteme);
7319 DEFINE_CHANNEL_OPTION(offchannel);
7320 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7322 /* Alias set topic to set defaulttopic for compatibility. */
7323 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7326 DEFINE_USER_OPTION(noautoop);
7327 DEFINE_USER_OPTION(autoinvite);
7328 DEFINE_USER_OPTION(info);
7330 /* Alias uset autovoice to uset autoop. */
7331 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7333 note_types = dict_new();
7334 dict_set_free_data(note_types, chanserv_deref_note_type);
7337 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7338 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7339 service_register(chanserv)->trigger = '!';
7340 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7342 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7344 if(chanserv_conf.channel_expire_frequency)
7345 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7347 if(chanserv_conf.refresh_period)
7349 time_t next_refresh;
7350 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7351 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7354 reg_exit_func(chanserv_db_cleanup);
7355 message_register_table(msgtab);