1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2006 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_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
201 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
204 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
205 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
206 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
207 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
208 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
209 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
210 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
211 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
212 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
213 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
214 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
215 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
216 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
217 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
218 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
219 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
221 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
223 /* Channel management */
224 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
225 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
226 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
228 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
229 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
230 { "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" },
231 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
232 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
233 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
234 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
236 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
237 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
238 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
239 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
240 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
241 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
242 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
243 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
244 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
245 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
246 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
247 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
248 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
249 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
250 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
251 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
252 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
253 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
254 { "CSMSG_SET_MODES", "$bModes $b %s" },
255 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
256 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
257 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
258 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
259 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
260 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
261 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
262 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
263 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
264 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
265 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
266 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
267 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
268 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
269 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
270 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
271 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
272 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
273 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
274 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
275 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
276 { "CSMSG_USET_INFO", "$bInfo $b %s" },
278 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
279 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
280 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
281 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
282 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
283 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
284 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
285 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
287 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
288 { "CSMSG_PROTECT_NONE", "No users will be protected." },
289 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
290 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
291 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
292 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
293 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
294 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
295 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
296 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
297 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
300 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
302 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
303 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
304 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
305 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
306 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
307 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
308 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
309 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
311 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
312 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
313 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
315 /* Channel userlist */
316 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
317 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
318 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
319 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
321 /* Channel note list */
322 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
323 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
324 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
325 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
326 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
327 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
328 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
329 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
330 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
331 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
332 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
333 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
334 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
335 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
336 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
338 /* Channel [un]suspension */
339 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
340 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
341 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
342 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
343 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
344 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
345 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
347 /* Access information */
348 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
349 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
350 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
351 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
352 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
353 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
354 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
355 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
356 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
357 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
358 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
360 /* Seen information */
361 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
362 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
363 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
364 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
366 /* Names information */
367 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
368 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
370 /* Channel information */
371 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
372 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
373 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
374 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
375 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
376 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
377 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
378 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
379 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
380 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
381 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
382 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
390 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
391 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
393 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
394 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
395 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
396 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
397 { "CSMSG_PEEK_OPS", "$bOps:$b" },
398 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
400 /* Network information */
401 { "CSMSG_NETWORK_INFO", "Network Information:" },
402 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
403 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
404 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
405 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
406 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
407 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
408 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
409 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
412 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
413 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
414 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
416 /* Channel searches */
417 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
418 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
419 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
420 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
422 /* Channel configuration */
423 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
424 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
425 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
426 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
429 { "CSMSG_USER_OPTIONS", "User Options:" },
430 { "CSMSG_USER_PROTECTED", "That user is protected." },
433 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
434 { "CSMSG_PING_RESPONSE", "Pong!" },
435 { "CSMSG_WUT_RESPONSE", "wut" },
436 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
437 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
438 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
439 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
440 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
441 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
442 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
445 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
449 /* eject_user and unban_user flags */
450 #define ACTION_KICK 0x0001
451 #define ACTION_BAN 0x0002
452 #define ACTION_ADD_BAN 0x0004
453 #define ACTION_ADD_TIMED_BAN 0x0008
454 #define ACTION_UNBAN 0x0010
455 #define ACTION_DEL_BAN 0x0020
457 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
458 #define MODELEN 40 + KEYLEN
462 #define CSFUNC_ARGS user, channel, argc, argv, cmd
464 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
465 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
466 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
467 reply("MSG_MISSING_PARAMS", argv[0]); \
471 DECLARE_LIST(dnrList, struct do_not_register *);
472 DEFINE_LIST(dnrList, struct do_not_register *);
474 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
476 struct userNode *chanserv;
479 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
480 static struct log_type *CS_LOG;
484 struct channelList support_channels;
485 struct mod_chanmode default_modes;
487 unsigned long db_backup_frequency;
488 unsigned long channel_expire_frequency;
491 unsigned int adjust_delay;
492 long channel_expire_delay;
493 unsigned int nodelete_level;
495 unsigned int adjust_threshold;
496 int join_flood_threshold;
498 unsigned int greeting_length;
499 unsigned int refresh_period;
500 unsigned int giveownership_period;
502 unsigned int max_owned;
503 unsigned int max_chan_users;
504 unsigned int max_chan_bans;
505 unsigned int max_userinfo_length;
507 struct string_list *set_shows;
508 struct string_list *eightball;
509 struct string_list *old_ban_names;
511 const char *ctcp_short_ban_duration;
512 const char *ctcp_long_ban_duration;
514 const char *irc_operator_epithet;
515 const char *network_helper_epithet;
516 const char *support_helper_epithet;
521 struct userNode *user;
522 struct userNode *bot;
523 struct chanNode *channel;
525 unsigned short lowest;
526 unsigned short highest;
527 struct userData **users;
528 struct helpfile_table table;
531 enum note_access_type
533 NOTE_SET_CHANNEL_ACCESS,
534 NOTE_SET_CHANNEL_SETTER,
538 enum note_visible_type
541 NOTE_VIS_CHANNEL_USERS,
547 enum note_access_type set_access_type;
549 unsigned int min_opserv;
550 unsigned short min_ulevel;
552 enum note_visible_type visible_type;
553 unsigned int max_length;
560 struct note_type *type;
561 char setter[NICKSERV_HANDLE_LEN+1];
565 static unsigned int registered_channels;
566 static unsigned int banCount;
568 static const struct {
571 unsigned short level;
574 { "peon", "Peon", UL_PEON, '+' },
575 { "op", "Op", UL_OP, '@' },
576 { "master", "Master", UL_MASTER, '%' },
577 { "coowner", "Coowner", UL_COOWNER, '*' },
578 { "owner", "Owner", UL_OWNER, '!' },
579 { "helper", "BUG:", UL_HELPER, 'X' }
582 static const struct {
585 unsigned short default_value;
586 unsigned int old_idx;
587 unsigned int old_flag;
588 unsigned short flag_value;
590 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
591 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
592 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
593 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
594 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
595 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
596 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
597 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
598 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
599 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
600 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
603 struct charOptionValues {
606 } protectValues[] = {
607 { 'a', "CSMSG_PROTECT_ALL" },
608 { 'e', "CSMSG_PROTECT_EQUAL" },
609 { 'l', "CSMSG_PROTECT_LOWER" },
610 { 'n', "CSMSG_PROTECT_NONE" }
612 { 'd', "CSMSG_TOYS_DISABLED" },
613 { 'n', "CSMSG_TOYS_PRIVATE" },
614 { 'p', "CSMSG_TOYS_PUBLIC" }
615 }, topicRefreshValues[] = {
616 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
617 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
618 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
619 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
620 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
621 }, ctcpReactionValues[] = {
622 { 'k', "CSMSG_CTCPREACTION_KICK" },
623 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
624 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
625 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
628 static const struct {
632 unsigned int old_idx;
634 struct charOptionValues *values;
636 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
637 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
638 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
639 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
642 struct userData *helperList;
643 struct chanData *channelList;
644 static struct module *chanserv_module;
645 static unsigned int userCount;
647 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
648 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
649 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
652 user_level_from_name(const char *name, unsigned short clamp_level)
654 unsigned int level = 0, ii;
656 level = strtoul(name, NULL, 10);
657 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
658 if(!irccasecmp(name, accessLevels[ii].name))
659 level = accessLevels[ii].level;
660 if(level > clamp_level)
666 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
669 *minl = strtoul(arg, &sep, 10);
677 *maxl = strtoul(sep+1, &sep, 10);
685 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
687 struct userData *uData, **head;
689 if(!channel || !handle)
692 if(override && HANDLE_FLAGGED(handle, HELPING)
693 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
695 for(uData = helperList;
696 uData && uData->handle != handle;
697 uData = uData->next);
701 uData = calloc(1, sizeof(struct userData));
702 uData->handle = handle;
704 uData->access = UL_HELPER;
710 uData->next = helperList;
712 helperList->prev = uData;
720 for(uData = channel->users; uData; uData = uData->next)
721 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
724 head = &(channel->users);
727 if(uData && (uData != *head))
729 /* Shuffle the user to the head of whatever list he was in. */
731 uData->next->prev = uData->prev;
733 uData->prev->next = uData->next;
739 (**head).prev = uData;
746 /* Returns non-zero if user has at least the minimum access.
747 * exempt_owner is set when handling !set, so the owner can set things
750 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
752 struct userData *uData;
753 struct chanData *cData = channel->channel_info;
754 unsigned short minimum = cData->lvlOpts[opt];
757 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
760 if(minimum <= uData->access)
762 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
767 /* Scan for other users authenticated to the same handle
768 still in the channel. If so, keep them listed as present.
770 user is optional, if not null, it skips checking that userNode
771 (for the handle_part function) */
773 scan_user_presence(struct userData *uData, struct userNode *user)
777 if(IsSuspended(uData->channel)
778 || IsUserSuspended(uData)
779 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
791 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
793 unsigned int eflags, argc;
795 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
797 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
798 if(!channel->channel_info
799 || IsSuspended(channel->channel_info)
801 || !ircncasecmp(text, "ACTION ", 7))
803 /* Figure out the minimum level needed to CTCP the channel */
804 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
806 /* We need to enforce against them; do so. */
808 argv[0] = (char*)text;
809 argv[1] = user->nick;
811 if(GetUserMode(channel, user))
812 eflags |= ACTION_KICK;
813 switch(channel->channel_info->chOpts[chCTCPReaction]) {
814 default: case 'k': /* just do the kick */ break;
816 eflags |= ACTION_BAN;
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
823 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
824 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
827 argv[argc++] = bad_ctcp_reason;
828 eject_user(chanserv, channel, argc, argv, NULL, eflags);
832 chanserv_create_note_type(const char *name)
834 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
835 strcpy(ntype->name, name);
837 dict_insert(note_types, ntype->name, ntype);
842 chanserv_deref_note_type(void *data)
844 struct note_type *ntype = data;
846 if(--ntype->refs > 0)
852 chanserv_flush_note_type(struct note_type *ntype)
854 struct chanData *cData;
855 for(cData = channelList; cData; cData = cData->next)
856 dict_remove(cData->notes, ntype->name);
860 chanserv_truncate_notes(struct note_type *ntype)
862 struct chanData *cData;
864 unsigned int size = sizeof(*note) + ntype->max_length;
866 for(cData = channelList; cData; cData = cData->next) {
867 note = dict_find(cData->notes, ntype->name, NULL);
870 if(strlen(note->note) <= ntype->max_length)
872 dict_remove2(cData->notes, ntype->name, 1);
873 note = realloc(note, size);
874 note->note[ntype->max_length] = 0;
875 dict_insert(cData->notes, ntype->name, note);
879 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
882 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
885 unsigned int len = strlen(text);
887 if(len > type->max_length) len = type->max_length;
888 note = calloc(1, sizeof(*note) + len);
890 strncpy(note->setter, setter, sizeof(note->setter)-1);
891 memcpy(note->note, text, len);
893 dict_insert(channel->notes, type->name, note);
899 chanserv_free_note(void *data)
901 struct note *note = data;
903 chanserv_deref_note_type(note->type);
904 assert(note->type->refs > 0); /* must use delnote to remove the type */
908 static MODCMD_FUNC(cmd_createnote) {
909 struct note_type *ntype;
910 unsigned int arg = 1, existed = 0, max_length;
912 if((ntype = dict_find(note_types, argv[1], NULL)))
915 ntype = chanserv_create_note_type(argv[arg]);
916 if(!irccasecmp(argv[++arg], "privileged"))
919 ntype->set_access_type = NOTE_SET_PRIVILEGED;
920 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
922 else if(!irccasecmp(argv[arg], "channel"))
924 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
927 reply("CSMSG_INVALID_ACCESS", argv[arg]);
930 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
931 ntype->set_access.min_ulevel = ulvl;
933 else if(!irccasecmp(argv[arg], "setter"))
935 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
939 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
943 if(!irccasecmp(argv[++arg], "privileged"))
944 ntype->visible_type = NOTE_VIS_PRIVILEGED;
945 else if(!irccasecmp(argv[arg], "channel_users"))
946 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
947 else if(!irccasecmp(argv[arg], "all"))
948 ntype->visible_type = NOTE_VIS_ALL;
950 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
954 if((arg+1) >= argc) {
955 reply("MSG_MISSING_PARAMS", argv[0]);
958 max_length = strtoul(argv[++arg], NULL, 0);
959 if(max_length < 20 || max_length > 450)
961 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
964 if(existed && (max_length < ntype->max_length))
966 ntype->max_length = max_length;
967 chanserv_truncate_notes(ntype);
969 ntype->max_length = max_length;
972 reply("CSMSG_NOTE_MODIFIED", ntype->name);
974 reply("CSMSG_NOTE_CREATED", ntype->name);
979 dict_remove(note_types, ntype->name);
983 static MODCMD_FUNC(cmd_removenote) {
984 struct note_type *ntype;
987 ntype = dict_find(note_types, argv[1], NULL);
988 force = (argc > 2) && !irccasecmp(argv[2], "force");
991 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
998 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1001 chanserv_flush_note_type(ntype);
1003 dict_remove(note_types, argv[1]);
1004 reply("CSMSG_NOTE_DELETED", argv[1]);
1009 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1013 if(orig->modes_set & change->modes_clear)
1015 if(orig->modes_clear & change->modes_set)
1017 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1018 && strcmp(orig->new_key, change->new_key))
1020 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1021 && (orig->new_limit != change->new_limit))
1026 static char max_length_text[MAXLEN+1][16];
1028 static struct helpfile_expansion
1029 chanserv_expand_variable(const char *variable)
1031 struct helpfile_expansion exp;
1033 if(!irccasecmp(variable, "notes"))
1036 exp.type = HF_TABLE;
1037 exp.value.table.length = 1;
1038 exp.value.table.width = 3;
1039 exp.value.table.flags = 0;
1040 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1041 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1042 exp.value.table.contents[0][0] = "Note Type";
1043 exp.value.table.contents[0][1] = "Visibility";
1044 exp.value.table.contents[0][2] = "Max Length";
1045 for(it=dict_first(note_types); it; it=iter_next(it))
1047 struct note_type *ntype = iter_data(it);
1050 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1051 row = exp.value.table.length++;
1052 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1053 exp.value.table.contents[row][0] = ntype->name;
1054 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1055 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1057 if(!max_length_text[ntype->max_length][0])
1058 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1059 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1064 exp.type = HF_STRING;
1065 exp.value.str = NULL;
1069 static struct chanData*
1070 register_channel(struct chanNode *cNode, char *registrar)
1072 struct chanData *channel;
1073 enum levelOption lvlOpt;
1074 enum charOption chOpt;
1076 channel = calloc(1, sizeof(struct chanData));
1078 channel->notes = dict_new();
1079 dict_set_free_data(channel->notes, chanserv_free_note);
1081 channel->registrar = strdup(registrar);
1082 channel->registered = now;
1083 channel->visited = now;
1084 channel->limitAdjusted = now;
1085 channel->ownerTransfer = now;
1086 channel->flags = CHANNEL_DEFAULT_FLAGS;
1087 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1088 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1089 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1090 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1092 channel->prev = NULL;
1093 channel->next = channelList;
1096 channelList->prev = channel;
1097 channelList = channel;
1098 registered_channels++;
1100 channel->channel = cNode;
1102 cNode->channel_info = channel;
1107 static struct userData*
1108 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1110 struct userData *ud;
1112 if(access > UL_OWNER)
1115 ud = calloc(1, sizeof(*ud));
1116 ud->channel = channel;
1117 ud->handle = handle;
1119 ud->access = access;
1120 ud->info = info ? strdup(info) : NULL;
1123 ud->next = channel->users;
1125 channel->users->prev = ud;
1126 channel->users = ud;
1128 channel->userCount++;
1132 ud->u_next = ud->handle->channels;
1134 ud->u_next->u_prev = ud;
1135 ud->handle->channels = ud;
1140 static void unregister_channel(struct chanData *channel, const char *reason);
1143 del_channel_user(struct userData *user, int do_gc)
1145 struct chanData *channel = user->channel;
1147 channel->userCount--;
1151 user->prev->next = user->next;
1153 channel->users = user->next;
1155 user->next->prev = user->prev;
1158 user->u_prev->u_next = user->u_next;
1160 user->handle->channels = user->u_next;
1162 user->u_next->u_prev = user->u_prev;
1166 if(do_gc && !channel->users && !IsProtected(channel))
1167 unregister_channel(channel, "lost all users.");
1170 static void expire_ban(void *data);
1172 static struct banData*
1173 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1176 unsigned int ii, l1, l2;
1181 bd = malloc(sizeof(struct banData));
1183 bd->channel = channel;
1185 bd->triggered = triggered;
1186 bd->expires = expires;
1188 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1190 extern const char *hidden_host_suffix;
1191 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1195 l2 = strlen(old_name);
1198 if(irccasecmp(mask + l1 - l2, old_name))
1200 new_mask = alloca(MAXLEN);
1201 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1204 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1206 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1207 bd->reason = strdup(reason);
1210 timeq_add(expires, expire_ban, bd);
1213 bd->next = channel->bans;
1215 channel->bans->prev = bd;
1217 channel->banCount++;
1224 del_channel_ban(struct banData *ban)
1226 ban->channel->banCount--;
1230 ban->prev->next = ban->next;
1232 ban->channel->bans = ban->next;
1235 ban->next->prev = ban->prev;
1238 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1247 expire_ban(void *data)
1249 struct banData *bd = data;
1250 if(!IsSuspended(bd->channel))
1252 struct banList bans;
1253 struct mod_chanmode change;
1255 bans = bd->channel->channel->banlist;
1256 mod_chanmode_init(&change);
1257 for(ii=0; ii<bans.used; ii++)
1259 if(!strcmp(bans.list[ii]->ban, bd->mask))
1262 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1263 change.args[0].u.hostmask = bd->mask;
1264 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1270 del_channel_ban(bd);
1273 static void chanserv_expire_suspension(void *data);
1276 unregister_channel(struct chanData *channel, const char *reason)
1278 struct mod_chanmode change;
1279 char msgbuf[MAXLEN];
1281 /* After channel unregistration, the following must be cleaned
1283 - Channel information.
1286 - Channel suspension data.
1287 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1293 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1297 mod_chanmode_init(&change);
1298 change.modes_clear |= MODE_REGISTERED;
1299 mod_chanmode_announce(chanserv, channel->channel, &change);
1302 while(channel->users)
1303 del_channel_user(channel->users, 0);
1305 while(channel->bans)
1306 del_channel_ban(channel->bans);
1308 free(channel->topic);
1309 free(channel->registrar);
1310 free(channel->greeting);
1311 free(channel->user_greeting);
1312 free(channel->topic_mask);
1315 channel->prev->next = channel->next;
1317 channelList = channel->next;
1320 channel->next->prev = channel->prev;
1322 if(channel->suspended)
1324 struct chanNode *cNode = channel->channel;
1325 struct suspended *suspended, *next_suspended;
1327 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1329 next_suspended = suspended->previous;
1330 free(suspended->suspender);
1331 free(suspended->reason);
1332 if(suspended->expires)
1333 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1338 cNode->channel_info = NULL;
1340 channel->channel->channel_info = NULL;
1342 dict_delete(channel->notes);
1343 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1344 if(!IsSuspended(channel))
1345 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1346 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1347 UnlockChannel(channel->channel);
1349 registered_channels--;
1353 expire_channels(UNUSED_ARG(void *data))
1355 struct chanData *channel, *next;
1356 struct userData *user;
1357 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1359 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1360 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1362 for(channel = channelList; channel; channel = next)
1364 next = channel->next;
1366 /* See if the channel can be expired. */
1367 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1368 || IsProtected(channel))
1371 /* Make sure there are no high-ranking users still in the channel. */
1372 for(user=channel->users; user; user=user->next)
1373 if(user->present && (user->access >= UL_PRESENT))
1378 /* Unregister the channel */
1379 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1380 unregister_channel(channel, "registration expired.");
1383 if(chanserv_conf.channel_expire_frequency)
1384 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1388 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1390 char protect = channel->chOpts[chProtect];
1391 struct userData *cs_victim, *cs_aggressor;
1393 /* Don't protect if no one is to be protected, someone is attacking
1394 himself, or if the aggressor is an IRC Operator. */
1395 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1398 /* Don't protect if the victim isn't authenticated (because they
1399 can't be a channel user), unless we are to protect non-users
1401 cs_victim = GetChannelAccess(channel, victim->handle_info);
1402 if(protect != 'a' && !cs_victim)
1405 /* Protect if the aggressor isn't a user because at this point,
1406 the aggressor can only be less than or equal to the victim. */
1407 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1411 /* If the aggressor was a user, then the victim can't be helped. */
1418 if(cs_victim->access > cs_aggressor->access)
1423 if(cs_victim->access >= cs_aggressor->access)
1432 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1434 struct chanData *cData = channel->channel_info;
1435 struct userData *cs_victim;
1437 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1438 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1439 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1441 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1449 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1451 if(IsService(victim))
1453 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1457 if(protect_user(victim, user, channel->channel_info))
1459 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1466 static struct do_not_register *
1467 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1469 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1470 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1471 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1472 strcpy(dnr->reason, reason);
1474 if(dnr->chan_name[0] == '*')
1475 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1476 else if(strpbrk(dnr->chan_name, "*?"))
1477 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1479 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1483 static struct dnrList
1484 chanserv_find_dnrs(const char *chan_name, const char *handle)
1486 struct dnrList list;
1488 struct do_not_register *dnr;
1490 dnrList_init(&list);
1491 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1492 dnrList_append(&list, dnr);
1493 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1494 dnrList_append(&list, dnr);
1496 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1497 if(match_ircglob(chan_name, iter_key(it)))
1498 dnrList_append(&list, iter_data(it));
1503 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1505 struct dnrList list;
1506 struct do_not_register *dnr;
1508 char buf[INTERVALLEN];
1510 list = chanserv_find_dnrs(chan_name, handle);
1511 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1513 dnr = list.list[ii];
1516 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1517 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1520 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1523 reply("CSMSG_MORE_DNRS", list.used - ii);
1528 struct do_not_register *
1529 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1531 struct do_not_register *dnr;
1534 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1538 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1540 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1541 if(match_ircglob(chan_name, iter_key(it)))
1542 return iter_data(it);
1547 static CHANSERV_FUNC(cmd_noregister)
1550 struct do_not_register *dnr;
1551 char buf[INTERVALLEN];
1552 unsigned int matches;
1558 reply("CSMSG_DNR_SEARCH_RESULTS");
1560 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1562 dnr = iter_data(it);
1564 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1566 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1569 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1571 dnr = iter_data(it);
1573 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1575 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1578 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1580 dnr = iter_data(it);
1582 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1584 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1589 reply("MSG_MATCH_COUNT", matches);
1591 reply("MSG_NO_MATCHES");
1597 if(!IsChannelName(target) && (*target != '*'))
1599 reply("CSMSG_NOT_DNR", target);
1605 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1606 if((*target == '*') && !get_handle_info(target + 1))
1608 reply("MSG_HANDLE_UNKNOWN", target + 1);
1611 chanserv_add_dnr(target, user->handle_info->handle, reason);
1612 reply("CSMSG_NOREGISTER_CHANNEL", target);
1616 reply("CSMSG_DNR_SEARCH_RESULTS");
1618 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1620 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1622 reply("MSG_NO_MATCHES");
1626 static CHANSERV_FUNC(cmd_allowregister)
1628 const char *chan_name = argv[1];
1630 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1632 dict_remove(handle_dnrs, chan_name+1);
1633 reply("CSMSG_DNR_REMOVED", chan_name);
1635 else if(dict_find(plain_dnrs, chan_name, NULL))
1637 dict_remove(plain_dnrs, chan_name);
1638 reply("CSMSG_DNR_REMOVED", chan_name);
1640 else if(dict_find(mask_dnrs, chan_name, NULL))
1642 dict_remove(mask_dnrs, chan_name);
1643 reply("CSMSG_DNR_REMOVED", chan_name);
1647 reply("CSMSG_NO_SUCH_DNR", chan_name);
1654 chanserv_get_owned_count(struct handle_info *hi)
1656 struct userData *cList;
1659 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1660 if(cList->access == UL_OWNER)
1665 static CHANSERV_FUNC(cmd_register)
1667 struct handle_info *handle;
1668 struct chanData *cData;
1669 struct modeNode *mn;
1670 char reason[MAXLEN];
1672 unsigned int new_channel, force=0;
1673 struct do_not_register *dnr;
1677 if(channel->channel_info)
1679 reply("CSMSG_ALREADY_REGGED", channel->name);
1683 if(channel->bad_channel)
1685 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1690 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1692 reply("CSMSG_MUST_BE_OPPED", channel->name);
1697 chan_name = channel->name;
1701 if((argc < 2) || !IsChannelName(argv[1]))
1703 reply("MSG_NOT_CHANNEL_NAME");
1707 if(opserv_bad_channel(argv[1]))
1709 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1714 chan_name = argv[1];
1717 if(argc >= (new_channel+2))
1719 if(!IsHelping(user))
1721 reply("CSMSG_PROXY_FORBIDDEN");
1725 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1727 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1728 dnr = chanserv_is_dnr(chan_name, handle);
1732 handle = user->handle_info;
1733 dnr = chanserv_is_dnr(chan_name, handle);
1737 if(!IsHelping(user))
1738 reply("CSMSG_DNR_CHANNEL", chan_name);
1740 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1744 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1746 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1751 channel = AddChannel(argv[1], now, NULL, NULL);
1753 cData = register_channel(channel, user->handle_info->handle);
1754 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1755 cData->modes = chanserv_conf.default_modes;
1757 cData->modes.modes_set |= MODE_REGISTERED;
1758 if (IsOffChannel(cData))
1760 mod_chanmode_announce(chanserv, channel, &cData->modes);
1764 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1765 change->args[change->argc].mode = MODE_CHANOP;
1766 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1768 mod_chanmode_announce(chanserv, channel, change);
1769 mod_chanmode_free(change);
1772 /* Initialize the channel's max user record. */
1773 cData->max = channel->members.used;
1775 if(handle != user->handle_info)
1776 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1778 reply("CSMSG_REG_SUCCESS", channel->name);
1780 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1781 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1786 make_confirmation_string(struct userData *uData)
1788 static char strbuf[16];
1793 for(src = uData->handle->handle; *src; )
1794 accum = accum * 31 + toupper(*src++);
1796 for(src = uData->channel->channel->name; *src; )
1797 accum = accum * 31 + toupper(*src++);
1798 sprintf(strbuf, "%08x", accum);
1802 static CHANSERV_FUNC(cmd_unregister)
1805 char reason[MAXLEN];
1806 struct chanData *cData;
1807 struct userData *uData;
1809 cData = channel->channel_info;
1812 reply("CSMSG_NOT_REGISTERED", channel->name);
1816 uData = GetChannelUser(cData, user->handle_info);
1817 if(!uData || (uData->access < UL_OWNER))
1819 reply("CSMSG_NO_ACCESS");
1823 if(IsProtected(cData))
1825 reply("CSMSG_UNREG_NODELETE", channel->name);
1829 if(!IsHelping(user))
1831 const char *confirm_string;
1832 if(IsSuspended(cData))
1834 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1837 confirm_string = make_confirmation_string(uData);
1838 if((argc < 2) || strcmp(argv[1], confirm_string))
1840 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1845 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1846 name = strdup(channel->name);
1847 unregister_channel(cData, reason);
1848 reply("CSMSG_UNREG_SUCCESS", name);
1853 static CHANSERV_FUNC(cmd_move)
1855 struct mod_chanmode change;
1856 struct chanNode *target;
1857 struct modeNode *mn;
1858 struct userData *uData;
1859 char reason[MAXLEN];
1860 struct do_not_register *dnr;
1864 if(IsProtected(channel->channel_info))
1866 reply("CSMSG_MOVE_NODELETE", channel->name);
1870 if(!IsChannelName(argv[1]))
1872 reply("MSG_NOT_CHANNEL_NAME");
1876 if(opserv_bad_channel(argv[1]))
1878 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1882 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1884 for(uData = channel->channel_info->users; uData; uData = uData->next)
1886 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1888 if(!IsHelping(user))
1889 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1891 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1897 mod_chanmode_init(&change);
1898 if(!(target = GetChannel(argv[1])))
1900 target = AddChannel(argv[1], now, NULL, NULL);
1901 if(!IsSuspended(channel->channel_info))
1902 AddChannelUser(chanserv, target);
1904 else if(target->channel_info)
1906 reply("CSMSG_ALREADY_REGGED", target->name);
1909 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1910 && !IsHelping(user))
1912 reply("CSMSG_MUST_BE_OPPED", target->name);
1915 else if(!IsSuspended(channel->channel_info))
1918 change.args[0].mode = MODE_CHANOP;
1919 change.args[0].u.member = AddChannelUser(chanserv, target);
1920 mod_chanmode_announce(chanserv, target, &change);
1925 /* Clear MODE_REGISTERED from old channel, add it to new. */
1927 change.modes_clear = MODE_REGISTERED;
1928 mod_chanmode_announce(chanserv, channel, &change);
1929 change.modes_clear = 0;
1930 change.modes_set = MODE_REGISTERED;
1931 mod_chanmode_announce(chanserv, target, &change);
1934 /* Move the channel_info to the target channel; it
1935 shouldn't be necessary to clear timeq callbacks
1936 for the old channel. */
1937 target->channel_info = channel->channel_info;
1938 target->channel_info->channel = target;
1939 channel->channel_info = NULL;
1941 reply("CSMSG_MOVE_SUCCESS", target->name);
1943 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1944 if(!IsSuspended(target->channel_info))
1946 char reason2[MAXLEN];
1947 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1948 DelChannelUser(chanserv, channel, reason2, 0);
1950 UnlockChannel(channel);
1951 LockChannel(target);
1952 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1957 merge_users(struct chanData *source, struct chanData *target)
1959 struct userData *suData, *tuData, *next;
1965 /* Insert the source's users into the scratch area. */
1966 for(suData = source->users; suData; suData = suData->next)
1967 dict_insert(merge, suData->handle->handle, suData);
1969 /* Iterate through the target's users, looking for
1970 users common to both channels. The lower access is
1971 removed from either the scratch area or target user
1973 for(tuData = target->users; tuData; tuData = next)
1975 struct userData *choice;
1977 next = tuData->next;
1979 /* If a source user exists with the same handle as a target
1980 channel's user, resolve the conflict by removing one. */
1981 suData = dict_find(merge, tuData->handle->handle, NULL);
1985 /* Pick the data we want to keep. */
1986 /* If the access is the same, use the later seen time. */
1987 if(suData->access == tuData->access)
1988 choice = (suData->seen > tuData->seen) ? suData : tuData;
1989 else /* Otherwise, keep the higher access level. */
1990 choice = (suData->access > tuData->access) ? suData : tuData;
1992 /* Remove the user that wasn't picked. */
1993 if(choice == tuData)
1995 dict_remove(merge, suData->handle->handle);
1996 del_channel_user(suData, 0);
1999 del_channel_user(tuData, 0);
2002 /* Move the remaining users to the target channel. */
2003 for(it = dict_first(merge); it; it = iter_next(it))
2005 suData = iter_data(it);
2007 /* Insert the user into the target channel's linked list. */
2008 suData->prev = NULL;
2009 suData->next = target->users;
2010 suData->channel = target;
2013 target->users->prev = suData;
2014 target->users = suData;
2016 /* Update the user counts for the target channel; the
2017 source counts are left alone. */
2018 target->userCount++;
2021 /* Possible to assert (source->users == NULL) here. */
2022 source->users = NULL;
2027 merge_bans(struct chanData *source, struct chanData *target)
2029 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2031 /* Hold on to the original head of the target ban list
2032 to avoid comparing source bans with source bans. */
2033 tFront = target->bans;
2035 /* Perform a totally expensive O(n*m) merge, ick. */
2036 for(sbData = source->bans; sbData; sbData = sNext)
2038 /* Flag to track whether the ban's been moved
2039 to the destination yet. */
2042 /* Possible to assert (sbData->prev == NULL) here. */
2043 sNext = sbData->next;
2045 for(tbData = tFront; tbData; tbData = tNext)
2047 tNext = tbData->next;
2049 /* Perform two comparisons between each source
2050 and target ban, conflicts are resolved by
2051 keeping the broader ban and copying the later
2052 expiration and triggered time. */
2053 if(match_ircglobs(tbData->mask, sbData->mask))
2055 /* There is a broader ban in the target channel that
2056 overrides one in the source channel; remove the
2057 source ban and break. */
2058 if(sbData->expires > tbData->expires)
2059 tbData->expires = sbData->expires;
2060 if(sbData->triggered > tbData->triggered)
2061 tbData->triggered = sbData->triggered;
2062 del_channel_ban(sbData);
2065 else if(match_ircglobs(sbData->mask, tbData->mask))
2067 /* There is a broader ban in the source channel that
2068 overrides one in the target channel; remove the
2069 target ban, fall through and move the source over. */
2070 if(tbData->expires > sbData->expires)
2071 sbData->expires = tbData->expires;
2072 if(tbData->triggered > sbData->triggered)
2073 sbData->triggered = tbData->triggered;
2074 if(tbData == tFront)
2076 del_channel_ban(tbData);
2079 /* Source bans can override multiple target bans, so
2080 we allow a source to run through this loop multiple
2081 times, but we can only move it once. */
2086 /* Remove the source ban from the source ban list. */
2088 sbData->next->prev = sbData->prev;
2090 /* Modify the source ban's associated channel. */
2091 sbData->channel = target;
2093 /* Insert the ban into the target channel's linked list. */
2094 sbData->prev = NULL;
2095 sbData->next = target->bans;
2098 target->bans->prev = sbData;
2099 target->bans = sbData;
2101 /* Update the user counts for the target channel. */
2106 /* Possible to assert (source->bans == NULL) here. */
2107 source->bans = NULL;
2111 merge_data(struct chanData *source, struct chanData *target)
2113 /* Use more recent visited and owner-transfer time; use older
2114 * registered time. Bitwise or may_opchan. Use higher max.
2115 * Do not touch last_refresh, ban count or user counts.
2117 if(source->visited > target->visited)
2118 target->visited = source->visited;
2119 if(source->registered < target->registered)
2120 target->registered = source->registered;
2121 if(source->ownerTransfer > target->ownerTransfer)
2122 target->ownerTransfer = source->ownerTransfer;
2123 if(source->may_opchan)
2124 target->may_opchan = 1;
2125 if(source->max > target->max)
2126 target->max = source->max;
2130 merge_channel(struct chanData *source, struct chanData *target)
2132 merge_users(source, target);
2133 merge_bans(source, target);
2134 merge_data(source, target);
2137 static CHANSERV_FUNC(cmd_merge)
2139 struct userData *target_user;
2140 struct chanNode *target;
2141 char reason[MAXLEN];
2145 /* Make sure the target channel exists and is registered to the user
2146 performing the command. */
2147 if(!(target = GetChannel(argv[1])))
2149 reply("MSG_INVALID_CHANNEL");
2153 if(!target->channel_info)
2155 reply("CSMSG_NOT_REGISTERED", target->name);
2159 if(IsProtected(channel->channel_info))
2161 reply("CSMSG_MERGE_NODELETE");
2165 if(IsSuspended(target->channel_info))
2167 reply("CSMSG_MERGE_SUSPENDED");
2171 if(channel == target)
2173 reply("CSMSG_MERGE_SELF");
2177 target_user = GetChannelUser(target->channel_info, user->handle_info);
2178 if(!target_user || (target_user->access < UL_OWNER))
2180 reply("CSMSG_MERGE_NOT_OWNER");
2184 /* Merge the channel structures and associated data. */
2185 merge_channel(channel->channel_info, target->channel_info);
2186 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2187 unregister_channel(channel->channel_info, reason);
2188 reply("CSMSG_MERGE_SUCCESS", target->name);
2192 static CHANSERV_FUNC(cmd_opchan)
2194 struct mod_chanmode change;
2195 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2197 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2200 channel->channel_info->may_opchan = 0;
2201 mod_chanmode_init(&change);
2203 change.args[0].mode = MODE_CHANOP;
2204 change.args[0].u.member = GetUserMode(channel, chanserv);
2205 mod_chanmode_announce(chanserv, channel, &change);
2206 reply("CSMSG_OPCHAN_DONE", channel->name);
2210 static CHANSERV_FUNC(cmd_adduser)
2212 struct userData *actee;
2213 struct userData *actor;
2214 struct handle_info *handle;
2215 unsigned short access;
2219 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2221 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2225 access = user_level_from_name(argv[2], UL_OWNER);
2228 reply("CSMSG_INVALID_ACCESS", argv[2]);
2232 actor = GetChannelUser(channel->channel_info, user->handle_info);
2233 if(actor->access <= access)
2235 reply("CSMSG_NO_BUMP_ACCESS");
2239 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2242 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2244 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2248 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2249 scan_user_presence(actee, NULL);
2250 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2254 static CHANSERV_FUNC(cmd_clvl)
2256 struct handle_info *handle;
2257 struct userData *victim;
2258 struct userData *actor;
2259 unsigned short new_access;
2260 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2264 actor = GetChannelUser(channel->channel_info, user->handle_info);
2266 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2269 if(handle == user->handle_info && !privileged)
2271 reply("CSMSG_NO_SELF_CLVL");
2275 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2277 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2281 if(actor->access <= victim->access && !privileged)
2283 reply("MSG_USER_OUTRANKED", handle->handle);
2287 new_access = user_level_from_name(argv[2], UL_OWNER);
2291 reply("CSMSG_INVALID_ACCESS", argv[2]);
2295 if(new_access >= actor->access && !privileged)
2297 reply("CSMSG_NO_BUMP_ACCESS");
2301 victim->access = new_access;
2302 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2306 static CHANSERV_FUNC(cmd_deluser)
2308 struct handle_info *handle;
2309 struct userData *victim;
2310 struct userData *actor;
2311 unsigned short access;
2316 actor = GetChannelUser(channel->channel_info, user->handle_info);
2318 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2321 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2323 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2329 access = user_level_from_name(argv[1], UL_OWNER);
2332 reply("CSMSG_INVALID_ACCESS", argv[1]);
2335 if(access != victim->access)
2337 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2343 access = victim->access;
2346 if((actor->access <= victim->access) && !IsHelping(user))
2348 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2352 chan_name = strdup(channel->name);
2353 del_channel_user(victim, 1);
2354 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2360 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2362 struct userData *actor, *uData, *next;
2364 actor = GetChannelUser(channel->channel_info, user->handle_info);
2366 if(min_access > max_access)
2368 reply("CSMSG_BAD_RANGE", min_access, max_access);
2372 if((actor->access <= max_access) && !IsHelping(user))
2374 reply("CSMSG_NO_ACCESS");
2378 for(uData = channel->channel_info->users; uData; uData = next)
2382 if((uData->access >= min_access)
2383 && (uData->access <= max_access)
2384 && match_ircglob(uData->handle->handle, mask))
2385 del_channel_user(uData, 1);
2388 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2392 static CHANSERV_FUNC(cmd_mdelowner)
2394 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2397 static CHANSERV_FUNC(cmd_mdelcoowner)
2399 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2402 static CHANSERV_FUNC(cmd_mdelmaster)
2404 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2407 static CHANSERV_FUNC(cmd_mdelop)
2409 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2412 static CHANSERV_FUNC(cmd_mdelpeon)
2414 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2418 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2420 struct banData *bData, *next;
2421 char interval[INTERVALLEN];
2426 limit = now - duration;
2427 for(bData = channel->channel_info->bans; bData; bData = next)
2431 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2434 del_channel_ban(bData);
2438 intervalString(interval, duration, user->handle_info);
2439 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2444 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2446 struct userData *actor, *uData, *next;
2447 char interval[INTERVALLEN];
2451 actor = GetChannelUser(channel->channel_info, user->handle_info);
2452 if(min_access > max_access)
2454 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2458 if((actor->access <= max_access) && !IsHelping(user))
2460 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2465 limit = now - duration;
2466 for(uData = channel->channel_info->users; uData; uData = next)
2470 if((uData->seen > limit)
2472 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2475 if(((uData->access >= min_access) && (uData->access <= max_access))
2476 || (!max_access && (uData->access < actor->access)))
2478 del_channel_user(uData, 1);
2486 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2488 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2492 static CHANSERV_FUNC(cmd_trim)
2494 unsigned long duration;
2495 unsigned short min_level, max_level;
2500 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2501 duration = ParseInterval(argv[2]);
2504 reply("CSMSG_CANNOT_TRIM");
2508 if(!irccasecmp(argv[1], "bans"))
2510 cmd_trim_bans(user, channel, duration);
2513 else if(!irccasecmp(argv[1], "users"))
2515 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2518 else if(parse_level_range(&min_level, &max_level, argv[1]))
2520 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2523 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2525 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2530 reply("CSMSG_INVALID_TRIM", argv[1]);
2535 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2536 to the user. cmd_all takes advantage of this. */
2537 static CHANSERV_FUNC(cmd_up)
2539 struct mod_chanmode change;
2540 struct userData *uData;
2543 mod_chanmode_init(&change);
2545 change.args[0].u.member = GetUserMode(channel, user);
2546 if(!change.args[0].u.member)
2549 reply("MSG_CHANNEL_ABSENT", channel->name);
2553 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2557 reply("CSMSG_GODMODE_UP", argv[0]);
2560 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2562 change.args[0].mode = MODE_CHANOP;
2563 errmsg = "CSMSG_ALREADY_OPPED";
2565 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2567 change.args[0].mode = MODE_VOICE;
2568 errmsg = "CSMSG_ALREADY_VOICED";
2573 reply("CSMSG_NO_ACCESS");
2576 change.args[0].mode &= ~change.args[0].u.member->modes;
2577 if(!change.args[0].mode)
2580 reply(errmsg, channel->name);
2583 modcmd_chanmode_announce(&change);
2587 static CHANSERV_FUNC(cmd_down)
2589 struct mod_chanmode change;
2591 mod_chanmode_init(&change);
2593 change.args[0].u.member = GetUserMode(channel, user);
2594 if(!change.args[0].u.member)
2597 reply("MSG_CHANNEL_ABSENT", channel->name);
2601 if(!change.args[0].u.member->modes)
2604 reply("CSMSG_ALREADY_DOWN", channel->name);
2608 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2609 modcmd_chanmode_announce(&change);
2613 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)
2615 struct userData *cList;
2617 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2619 if(IsSuspended(cList->channel)
2620 || IsUserSuspended(cList)
2621 || !GetUserMode(cList->channel->channel, user))
2624 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2630 static CHANSERV_FUNC(cmd_upall)
2632 return cmd_all(CSFUNC_ARGS, cmd_up);
2635 static CHANSERV_FUNC(cmd_downall)
2637 return cmd_all(CSFUNC_ARGS, cmd_down);
2640 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2641 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2644 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)
2646 unsigned int ii, valid;
2647 struct userNode *victim;
2648 struct mod_chanmode *change;
2650 change = mod_chanmode_alloc(argc - 1);
2652 for(ii=valid=0; ++ii < argc; )
2654 if(!(victim = GetUserH(argv[ii])))
2656 change->args[valid].mode = mode;
2657 change->args[valid].u.member = GetUserMode(channel, victim);
2658 if(!change->args[valid].u.member)
2660 if(validate && !validate(user, channel, victim))
2665 change->argc = valid;
2666 if(valid < (argc-1))
2667 reply("CSMSG_PROCESS_FAILED");
2670 modcmd_chanmode_announce(change);
2671 reply(action, channel->name);
2673 mod_chanmode_free(change);
2677 static CHANSERV_FUNC(cmd_op)
2679 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2682 static CHANSERV_FUNC(cmd_deop)
2684 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2687 static CHANSERV_FUNC(cmd_voice)
2689 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2692 static CHANSERV_FUNC(cmd_devoice)
2694 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2698 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2704 for(ii=0; ii<channel->members.used; ii++)
2706 struct modeNode *mn = channel->members.list[ii];
2708 if(IsService(mn->user))
2711 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2714 if(protect_user(mn->user, user, channel->channel_info))
2718 victims[(*victimCount)++] = mn;
2724 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2726 struct userNode *victim;
2727 struct modeNode **victims;
2728 unsigned int offset, n, victimCount, duration = 0;
2729 char *reason = "Bye.", *ban, *name;
2730 char interval[INTERVALLEN];
2732 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2733 REQUIRE_PARAMS(offset);
2736 reason = unsplit_string(argv + offset, argc - offset, NULL);
2737 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2739 /* Truncate the reason to a length of TOPICLEN, as
2740 the ircd does; however, leave room for an ellipsis
2741 and the kicker's nick. */
2742 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2746 if((victim = GetUserH(argv[1])))
2748 victims = alloca(sizeof(victims[0]));
2749 victims[0] = GetUserMode(channel, victim);
2750 /* XXX: The comparison with ACTION_KICK is just because all
2751 * other actions can work on users outside the channel, and we
2752 * want to allow those (e.g. unbans) in that case. If we add
2753 * some other ejection action for in-channel users, change
2755 victimCount = victims[0] ? 1 : 0;
2757 if(IsService(victim))
2759 reply("MSG_SERVICE_IMMUNE", victim->nick);
2763 if((action == ACTION_KICK) && !victimCount)
2765 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2769 if(protect_user(victim, user, channel->channel_info))
2771 reply("CSMSG_USER_PROTECTED", victim->nick);
2775 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2776 name = victim->nick;
2780 if(!is_ircmask(argv[1]))
2782 reply("MSG_NICK_UNKNOWN", argv[1]);
2786 victims = alloca(sizeof(victims[0]) * channel->members.used);
2788 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2790 reply("CSMSG_MASK_PROTECTED", argv[1]);
2794 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2796 reply("CSMSG_LAME_MASK", argv[1]);
2800 if((action == ACTION_KICK) && (victimCount == 0))
2802 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2806 name = ban = strdup(argv[1]);
2809 /* Truncate the ban in place if necessary; we must ensure
2810 that 'ban' is a valid ban mask before sanitizing it. */
2811 sanitize_ircmask(ban);
2813 if(action & ACTION_ADD_BAN)
2815 struct banData *bData, *next;
2817 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2819 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2824 if(action & ACTION_ADD_TIMED_BAN)
2826 duration = ParseInterval(argv[2]);
2830 reply("CSMSG_DURATION_TOO_LOW");
2834 else if(duration > (86400 * 365 * 2))
2836 reply("CSMSG_DURATION_TOO_HIGH");
2842 for(bData = channel->channel_info->bans; bData; bData = next)
2844 if(match_ircglobs(bData->mask, ban))
2846 int exact = !irccasecmp(bData->mask, ban);
2848 /* The ban is redundant; there is already a ban
2849 with the same effect in place. */
2853 free(bData->reason);
2854 bData->reason = strdup(reason);
2855 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2857 reply("CSMSG_REASON_CHANGE", ban);
2861 if(exact && bData->expires)
2865 /* If the ban matches an existing one exactly,
2866 extend the expiration time if the provided
2867 duration is longer. */
2868 if(duration && ((time_t)(now + duration) > bData->expires))
2870 bData->expires = now + duration;
2881 /* Delete the expiration timeq entry and
2882 requeue if necessary. */
2883 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2886 timeq_add(bData->expires, expire_ban, bData);
2890 /* automated kickban */
2893 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2895 reply("CSMSG_BAN_ADDED", name, channel->name);
2901 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2908 if(match_ircglobs(ban, bData->mask))
2910 /* The ban we are adding makes previously existing
2911 bans redundant; silently remove them. */
2912 del_channel_ban(bData);
2916 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);
2918 name = ban = strdup(bData->mask);
2922 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2924 extern const char *hidden_host_suffix;
2925 const char *old_name = chanserv_conf.old_ban_names->list[n];
2927 unsigned int l1, l2;
2930 l2 = strlen(old_name);
2933 if(irccasecmp(ban + l1 - l2, old_name))
2935 new_mask = malloc(MAXLEN);
2936 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2938 name = ban = new_mask;
2943 if(action & ACTION_BAN)
2945 unsigned int exists;
2946 struct mod_chanmode *change;
2948 if(channel->banlist.used >= MAXBANS)
2951 reply("CSMSG_BANLIST_FULL", channel->name);
2956 exists = ChannelBanExists(channel, ban);
2957 change = mod_chanmode_alloc(victimCount + 1);
2958 for(n = 0; n < victimCount; ++n)
2960 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2961 change->args[n].u.member = victims[n];
2965 change->args[n].mode = MODE_BAN;
2966 change->args[n++].u.hostmask = ban;
2970 modcmd_chanmode_announce(change);
2972 mod_chanmode_announce(chanserv, channel, change);
2973 mod_chanmode_free(change);
2975 if(exists && (action == ACTION_BAN))
2978 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2984 if(action & ACTION_KICK)
2986 char kick_reason[MAXLEN];
2987 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2989 for(n = 0; n < victimCount; n++)
2990 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2995 /* No response, since it was automated. */
2997 else if(action & ACTION_ADD_BAN)
3000 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3002 reply("CSMSG_BAN_ADDED", name, channel->name);
3004 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3005 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3006 else if(action & ACTION_BAN)
3007 reply("CSMSG_BAN_DONE", name, channel->name);
3008 else if(action & ACTION_KICK && victimCount)
3009 reply("CSMSG_KICK_DONE", name, channel->name);
3015 static CHANSERV_FUNC(cmd_kickban)
3017 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3020 static CHANSERV_FUNC(cmd_kick)
3022 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3025 static CHANSERV_FUNC(cmd_ban)
3027 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3030 static CHANSERV_FUNC(cmd_addban)
3032 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3035 static CHANSERV_FUNC(cmd_addtimedban)
3037 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3040 static struct mod_chanmode *
3041 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3043 struct mod_chanmode *change;
3044 unsigned char *match;
3045 unsigned int ii, count;
3047 match = alloca(bans->used);
3050 for(ii = count = 0; ii < bans->used; ++ii)
3052 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3053 MATCH_USENICK | MATCH_VISIBLE);
3060 for(ii = count = 0; ii < bans->used; ++ii)
3062 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3069 change = mod_chanmode_alloc(count);
3070 for(ii = count = 0; ii < bans->used; ++ii)
3074 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3075 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3077 assert(count == change->argc);
3082 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3084 struct userNode *actee;
3090 /* may want to allow a comma delimited list of users... */
3091 if(!(actee = GetUserH(argv[1])))
3093 if(!is_ircmask(argv[1]))
3095 reply("MSG_NICK_UNKNOWN", argv[1]);
3099 mask = strdup(argv[1]);
3102 /* We don't sanitize the mask here because ircu
3104 if(action & ACTION_UNBAN)
3106 struct mod_chanmode *change;
3107 change = find_matching_bans(&channel->banlist, actee, mask);
3112 modcmd_chanmode_announce(change);
3113 for(ii = 0; ii < change->argc; ++ii)
3114 free((char*)change->args[ii].u.hostmask);
3115 mod_chanmode_free(change);
3120 if(action & ACTION_DEL_BAN)
3122 struct banData *ban, *next;
3124 ban = channel->channel_info->bans;
3128 for( ; ban && !user_matches_glob(actee, ban->mask,
3129 MATCH_USENICK | MATCH_VISIBLE);
3132 for( ; ban && !match_ircglobs(mask, ban->mask);
3137 del_channel_ban(ban);
3144 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3146 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3152 static CHANSERV_FUNC(cmd_unban)
3154 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3157 static CHANSERV_FUNC(cmd_delban)
3159 /* it doesn't necessarily have to remove the channel ban - may want
3160 to make that an option. */
3161 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3164 static CHANSERV_FUNC(cmd_unbanme)
3166 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3167 long flags = ACTION_UNBAN;
3169 /* remove permanent bans if the user has the proper access. */
3170 if(uData->access >= UL_MASTER)
3171 flags |= ACTION_DEL_BAN;
3173 argv[1] = user->nick;
3174 return unban_user(user, channel, 2, argv, cmd, flags);
3177 static CHANSERV_FUNC(cmd_unbanall)
3179 struct mod_chanmode *change;
3182 if(!channel->banlist.used)
3184 reply("CSMSG_NO_BANS", channel->name);
3188 change = mod_chanmode_alloc(channel->banlist.used);
3189 for(ii=0; ii<channel->banlist.used; ii++)
3191 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3192 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3194 modcmd_chanmode_announce(change);
3195 for(ii = 0; ii < change->argc; ++ii)
3196 free((char*)change->args[ii].u.hostmask);
3197 mod_chanmode_free(change);
3198 reply("CSMSG_BANS_REMOVED", channel->name);
3202 static CHANSERV_FUNC(cmd_open)
3204 struct mod_chanmode *change;
3207 change = find_matching_bans(&channel->banlist, user, NULL);
3209 change = mod_chanmode_alloc(0);
3210 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3211 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3212 && channel->channel_info->modes.modes_set)
3213 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3214 modcmd_chanmode_announce(change);
3215 reply("CSMSG_CHANNEL_OPENED", channel->name);
3216 for(ii = 0; ii < change->argc; ++ii)
3217 free((char*)change->args[ii].u.hostmask);
3218 mod_chanmode_free(change);
3222 static CHANSERV_FUNC(cmd_myaccess)
3224 static struct string_buffer sbuf;
3225 struct handle_info *target_handle;
3226 struct userData *uData;
3229 target_handle = user->handle_info;
3230 else if(!IsHelping(user))
3232 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3235 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3238 if(!target_handle->channels)
3240 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3244 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3245 for(uData = target_handle->channels; uData; uData = uData->u_next)
3247 struct chanData *cData = uData->channel;
3249 if(uData->access > UL_OWNER)
3251 if(IsProtected(cData)
3252 && (target_handle != user->handle_info)
3253 && !GetTrueChannelAccess(cData, user->handle_info))
3256 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3257 if(uData->flags != USER_AUTO_OP)
3258 string_buffer_append(&sbuf, ',');
3259 if(IsUserSuspended(uData))
3260 string_buffer_append(&sbuf, 's');
3261 if(IsUserAutoOp(uData))
3263 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3264 string_buffer_append(&sbuf, 'o');
3265 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3266 string_buffer_append(&sbuf, 'v');
3268 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3269 string_buffer_append(&sbuf, 'i');
3271 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3273 string_buffer_append_string(&sbuf, ")]");
3274 string_buffer_append(&sbuf, '\0');
3275 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3281 static CHANSERV_FUNC(cmd_access)
3283 struct userNode *target;
3284 struct handle_info *target_handle;
3285 struct userData *uData;
3287 char prefix[MAXLEN];
3292 target_handle = target->handle_info;
3294 else if((target = GetUserH(argv[1])))
3296 target_handle = target->handle_info;
3298 else if(argv[1][0] == '*')
3300 if(!(target_handle = get_handle_info(argv[1]+1)))
3302 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3308 reply("MSG_NICK_UNKNOWN", argv[1]);
3312 assert(target || target_handle);
3314 if(target == chanserv)
3316 reply("CSMSG_IS_CHANSERV");
3324 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3329 reply("MSG_USER_AUTHENTICATE", target->nick);
3332 reply("MSG_AUTHENTICATE");
3338 const char *epithet = NULL, *type = NULL;
3341 epithet = chanserv_conf.irc_operator_epithet;
3344 else if(IsNetworkHelper(target))
3346 epithet = chanserv_conf.network_helper_epithet;
3347 type = "network helper";
3349 else if(IsSupportHelper(target))
3351 epithet = chanserv_conf.support_helper_epithet;
3352 type = "support helper";
3356 if(target_handle->epithet)
3357 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3359 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3361 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3365 sprintf(prefix, "%s", target_handle->handle);
3368 if(!channel->channel_info)
3370 reply("CSMSG_NOT_REGISTERED", channel->name);
3374 helping = HANDLE_FLAGGED(target_handle, HELPING)
3375 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3376 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3378 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3379 /* To prevent possible information leaks, only show infolines
3380 * if the requestor is in the channel or it's their own
3382 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3384 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3386 /* Likewise, only say it's suspended if the user has active
3387 * access in that channel or it's their own entry. */
3388 if(IsUserSuspended(uData)
3389 && (GetChannelUser(channel->channel_info, user->handle_info)
3390 || (user->handle_info == uData->handle)))
3392 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3397 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3404 zoot_list(struct listData *list)
3406 struct userData *uData;
3407 unsigned int start, curr, highest, lowest;
3408 struct helpfile_table tmp_table;
3409 const char **temp, *msg;
3411 if(list->table.length == 1)
3414 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3416 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3417 msg = user_find_message(list->user, "MSG_NONE");
3418 send_message_type(4, list->user, list->bot, " %s", msg);
3420 tmp_table.width = list->table.width;
3421 tmp_table.flags = list->table.flags;
3422 list->table.contents[0][0] = " ";
3423 highest = list->highest;
3424 if(list->lowest != 0)
3425 lowest = list->lowest;
3426 else if(highest < 100)
3429 lowest = highest - 100;
3430 for(start = curr = 1; curr < list->table.length; )
3432 uData = list->users[curr-1];
3433 list->table.contents[curr++][0] = " ";
3434 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3437 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3439 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3440 temp = list->table.contents[--start];
3441 list->table.contents[start] = list->table.contents[0];
3442 tmp_table.contents = list->table.contents + start;
3443 tmp_table.length = curr - start;
3444 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3445 list->table.contents[start] = temp;
3447 highest = lowest - 1;
3448 lowest = (highest < 100) ? 0 : (highest - 99);
3454 def_list(struct listData *list)
3458 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3460 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3461 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3462 if(list->table.length == 1)
3464 msg = user_find_message(list->user, "MSG_NONE");
3465 send_message_type(4, list->user, list->bot, " %s", msg);
3470 userData_access_comp(const void *arg_a, const void *arg_b)
3472 const struct userData *a = *(struct userData**)arg_a;
3473 const struct userData *b = *(struct userData**)arg_b;
3475 if(a->access != b->access)
3476 res = b->access - a->access;
3478 res = irccasecmp(a->handle->handle, b->handle->handle);
3483 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3485 void (*send_list)(struct listData *);
3486 struct userData *uData;
3487 struct listData lData;
3488 unsigned int matches;
3492 lData.bot = cmd->parent->bot;
3493 lData.channel = channel;
3494 lData.lowest = lowest;
3495 lData.highest = highest;
3496 lData.search = (argc > 1) ? argv[1] : NULL;
3497 send_list = def_list;
3498 (void)zoot_list; /* since it doesn't show user levels */
3500 if(user->handle_info)
3502 switch(user->handle_info->userlist_style)
3504 case HI_STYLE_DEF: send_list = def_list; break;
3505 case HI_STYLE_ZOOT: send_list = def_list; break;
3509 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3511 for(uData = channel->channel_info->users; uData; uData = uData->next)
3513 if((uData->access < lowest)
3514 || (uData->access > highest)
3515 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3517 lData.users[matches++] = uData;
3519 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3521 lData.table.length = matches+1;
3522 lData.table.width = 4;
3523 lData.table.flags = TABLE_NO_FREE;
3524 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3525 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3526 lData.table.contents[0] = ary;
3529 ary[2] = "Last Seen";
3531 for(matches = 1; matches < lData.table.length; ++matches)
3533 struct userData *uData = lData.users[matches-1];
3534 char seen[INTERVALLEN];
3536 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3537 lData.table.contents[matches] = ary;
3538 ary[0] = strtab(uData->access);
3539 ary[1] = uData->handle->handle;
3542 else if(!uData->seen)
3545 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3546 ary[2] = strdup(ary[2]);
3547 if(IsUserSuspended(uData))
3548 ary[3] = "Suspended";
3549 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3550 ary[3] = "Vacation";
3555 for(matches = 1; matches < lData.table.length; ++matches)
3557 free((char*)lData.table.contents[matches][2]);
3558 free(lData.table.contents[matches]);
3560 free(lData.table.contents[0]);
3561 free(lData.table.contents);
3565 static CHANSERV_FUNC(cmd_users)
3567 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3570 static CHANSERV_FUNC(cmd_wlist)
3572 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3575 static CHANSERV_FUNC(cmd_clist)
3577 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3580 static CHANSERV_FUNC(cmd_mlist)
3582 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3585 static CHANSERV_FUNC(cmd_olist)
3587 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3590 static CHANSERV_FUNC(cmd_plist)
3592 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3595 static CHANSERV_FUNC(cmd_bans)
3597 struct userNode *search_u = NULL;
3598 struct helpfile_table tbl;
3599 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3600 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3601 const char *msg_never, *triggered, *expires;
3602 struct banData *ban, **bans;
3606 else if(strchr(search = argv[1], '!'))
3609 search_wilds = search[strcspn(search, "?*")];
3611 else if(!(search_u = GetUserH(search)))
3612 reply("MSG_NICK_UNKNOWN", search);
3614 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3616 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3620 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3625 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3628 bans[matches++] = ban;
3633 tbl.length = matches + 1;
3634 tbl.width = 4 + timed;
3636 tbl.flags = TABLE_NO_FREE;
3637 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3638 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3639 tbl.contents[0][0] = "Mask";
3640 tbl.contents[0][1] = "Set By";
3641 tbl.contents[0][2] = "Triggered";
3644 tbl.contents[0][3] = "Expires";
3645 tbl.contents[0][4] = "Reason";
3648 tbl.contents[0][3] = "Reason";
3651 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3653 free(tbl.contents[0]);
3658 msg_never = user_find_message(user, "MSG_NEVER");
3659 for(ii = 0; ii < matches; )
3665 else if(ban->expires)
3666 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3668 expires = msg_never;
3671 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3673 triggered = msg_never;
3675 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3676 tbl.contents[ii][0] = ban->mask;
3677 tbl.contents[ii][1] = ban->owner;
3678 tbl.contents[ii][2] = strdup(triggered);
3681 tbl.contents[ii][3] = strdup(expires);
3682 tbl.contents[ii][4] = ban->reason;
3685 tbl.contents[ii][3] = ban->reason;
3687 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3688 reply("MSG_MATCH_COUNT", matches);
3689 for(ii = 1; ii < tbl.length; ++ii)
3691 free((char*)tbl.contents[ii][2]);
3693 free((char*)tbl.contents[ii][3]);
3694 free(tbl.contents[ii]);
3696 free(tbl.contents[0]);
3702 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3704 struct chanData *cData = channel->channel_info;
3705 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3707 if(cData->topic_mask)
3708 return !match_ircglob(new_topic, cData->topic_mask);
3709 else if(cData->topic)
3710 return irccasecmp(new_topic, cData->topic);
3715 static CHANSERV_FUNC(cmd_topic)
3717 struct chanData *cData;
3720 cData = channel->channel_info;
3725 SetChannelTopic(channel, chanserv, cData->topic, 1);
3726 reply("CSMSG_TOPIC_SET", cData->topic);
3730 reply("CSMSG_NO_TOPIC", channel->name);
3734 topic = unsplit_string(argv + 1, argc - 1, NULL);
3735 /* If they say "!topic *", use an empty topic. */
3736 if((topic[0] == '*') && (topic[1] == 0))
3738 if(bad_topic(channel, user, topic))
3740 char *topic_mask = cData->topic_mask;
3743 char new_topic[TOPICLEN+1], tchar;
3744 int pos=0, starpos=-1, dpos=0, len;
3746 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3753 len = strlen(topic);
3754 if((dpos + len) > TOPICLEN)
3755 len = TOPICLEN + 1 - dpos;
3756 memcpy(new_topic+dpos, topic, len);
3760 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3761 default: new_topic[dpos++] = tchar; break;
3764 if((dpos > TOPICLEN) || tchar)
3767 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3768 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3771 new_topic[dpos] = 0;
3772 SetChannelTopic(channel, chanserv, new_topic, 1);
3774 reply("CSMSG_TOPIC_LOCKED", channel->name);
3779 SetChannelTopic(channel, chanserv, topic, 1);
3781 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3783 /* Grab the topic and save it as the default topic. */
3785 cData->topic = strdup(channel->topic);
3791 static CHANSERV_FUNC(cmd_mode)
3793 struct userData *uData;
3794 struct mod_chanmode *change;
3799 change = &channel->channel_info->modes;
3800 if(change->modes_set || change->modes_clear) {
3801 modcmd_chanmode_announce(change);
3802 reply("CSMSG_DEFAULTED_MODES", channel->name);
3804 reply("CSMSG_NO_MODES", channel->name);
3808 uData = GetChannelUser(channel->channel_info, user->handle_info);
3810 base_oplevel = MAXOPLEVEL;
3811 else if (uData->access >= UL_OWNER)
3814 base_oplevel = 1 + UL_OWNER - uData->access;
3815 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3818 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3822 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3823 && mode_lock_violated(&channel->channel_info->modes, change))
3826 mod_chanmode_format(&channel->channel_info->modes, modes);
3827 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3831 modcmd_chanmode_announce(change);
3832 mod_chanmode_free(change);
3833 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3837 static CHANSERV_FUNC(cmd_invite)
3839 struct userData *uData;
3840 struct userNode *invite;
3842 uData = GetChannelUser(channel->channel_info, user->handle_info);
3846 if(!(invite = GetUserH(argv[1])))
3848 reply("MSG_NICK_UNKNOWN", argv[1]);
3855 if(GetUserMode(channel, invite))
3857 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3865 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3866 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3869 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3871 irc_invite(chanserv, invite, channel);
3873 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3878 static CHANSERV_FUNC(cmd_inviteme)
3880 if(GetUserMode(channel, user))
3882 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3885 if(channel->channel_info
3886 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3888 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3891 irc_invite(cmd->parent->bot, user, channel);
3896 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3899 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3901 /* We display things based on two dimensions:
3902 * - Issue time: present or absent
3903 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3904 * (in order of precedence, so something both expired and revoked
3905 * only counts as revoked)
3907 combo = (suspended->issued ? 4 : 0)
3908 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3910 case 0: /* no issue time, indefinite expiration */
3911 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3913 case 1: /* no issue time, expires in future */
3914 intervalString(buf1, suspended->expires-now, user->handle_info);
3915 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3917 case 2: /* no issue time, expired */
3918 intervalString(buf1, now-suspended->expires, user->handle_info);
3919 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3921 case 3: /* no issue time, revoked */
3922 intervalString(buf1, now-suspended->revoked, user->handle_info);
3923 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3925 case 4: /* issue time set, indefinite expiration */
3926 intervalString(buf1, now-suspended->issued, user->handle_info);
3927 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3929 case 5: /* issue time set, expires in future */
3930 intervalString(buf1, now-suspended->issued, user->handle_info);
3931 intervalString(buf2, suspended->expires-now, user->handle_info);
3932 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3934 case 6: /* issue time set, expired */
3935 intervalString(buf1, now-suspended->issued, user->handle_info);
3936 intervalString(buf2, now-suspended->expires, user->handle_info);
3937 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3939 case 7: /* issue time set, revoked */
3940 intervalString(buf1, now-suspended->issued, user->handle_info);
3941 intervalString(buf2, now-suspended->revoked, user->handle_info);
3942 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3945 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3950 static CHANSERV_FUNC(cmd_info)
3952 char modes[MAXLEN], buffer[INTERVALLEN];
3953 struct userData *uData, *owner;
3954 struct chanData *cData;
3955 struct do_not_register *dnr;
3960 cData = channel->channel_info;
3961 reply("CSMSG_CHANNEL_INFO", channel->name);
3963 uData = GetChannelUser(cData, user->handle_info);
3964 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3966 mod_chanmode_format(&cData->modes, modes);
3967 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3968 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3971 for(it = dict_first(cData->notes); it; it = iter_next(it))
3975 note = iter_data(it);
3976 if(!note_type_visible_to_user(cData, note->type, user))
3979 padding = PADLEN - 1 - strlen(iter_key(it));
3980 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3983 reply("CSMSG_CHANNEL_MAX", cData->max);
3984 for(owner = cData->users; owner; owner = owner->next)
3985 if(owner->access == UL_OWNER)
3986 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3987 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3988 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3989 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3991 privileged = IsStaff(user);
3993 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3994 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3995 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3997 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3998 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4000 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4002 struct suspended *suspended;
4003 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4004 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4005 show_suspension_info(cmd, user, suspended);
4007 else if(IsSuspended(cData))
4009 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4010 show_suspension_info(cmd, user, cData->suspended);
4015 static CHANSERV_FUNC(cmd_netinfo)
4017 extern time_t boot_time;
4018 extern unsigned long burst_length;
4019 char interval[INTERVALLEN];
4021 reply("CSMSG_NETWORK_INFO");
4022 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4023 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4024 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4025 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4026 reply("CSMSG_NETWORK_BANS", banCount);
4027 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4028 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4029 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4034 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4036 struct helpfile_table table;
4038 struct userNode *user;
4043 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4044 table.contents = alloca(list->used*sizeof(*table.contents));
4045 for(nn=0; nn<list->used; nn++)
4047 user = list->list[nn];
4048 if(user->modes & skip_flags)
4052 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4055 nick = alloca(strlen(user->nick)+3);
4056 sprintf(nick, "(%s)", user->nick);
4060 table.contents[table.length][0] = nick;
4063 table_send(chanserv, to->nick, 0, NULL, table);
4066 static CHANSERV_FUNC(cmd_ircops)
4068 reply("CSMSG_STAFF_OPERS");
4069 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4073 static CHANSERV_FUNC(cmd_helpers)
4075 reply("CSMSG_STAFF_HELPERS");
4076 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4080 static CHANSERV_FUNC(cmd_staff)
4082 reply("CSMSG_NETWORK_STAFF");
4083 cmd_ircops(CSFUNC_ARGS);
4084 cmd_helpers(CSFUNC_ARGS);
4088 static CHANSERV_FUNC(cmd_peek)
4090 struct modeNode *mn;
4091 char modes[MODELEN];
4093 struct helpfile_table table;
4095 irc_make_chanmode(channel, modes);
4097 reply("CSMSG_PEEK_INFO", channel->name);
4098 reply("CSMSG_PEEK_TOPIC", channel->topic);
4099 reply("CSMSG_PEEK_MODES", modes);
4100 reply("CSMSG_PEEK_USERS", channel->members.used);
4104 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4105 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4106 for(n = 0; n < channel->members.used; n++)
4108 mn = channel->members.list[n];
4109 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4111 table.contents[table.length] = alloca(sizeof(**table.contents));
4112 table.contents[table.length][0] = mn->user->nick;
4117 reply("CSMSG_PEEK_OPS");
4118 table_send(chanserv, user->nick, 0, NULL, table);
4121 reply("CSMSG_PEEK_NO_OPS");
4125 static MODCMD_FUNC(cmd_wipeinfo)
4127 struct handle_info *victim;
4128 struct userData *ud, *actor;
4131 actor = GetChannelUser(channel->channel_info, user->handle_info);
4132 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4134 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4136 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4139 if((ud->access >= actor->access) && (ud != actor))
4141 reply("MSG_USER_OUTRANKED", victim->handle);
4147 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4151 static CHANSERV_FUNC(cmd_resync)
4153 struct mod_chanmode *changes;
4154 struct chanData *cData = channel->channel_info;
4155 unsigned int ii, used;
4157 changes = mod_chanmode_alloc(channel->members.used * 2);
4158 for(ii = used = 0; ii < channel->members.used; ++ii)
4160 struct modeNode *mn = channel->members.list[ii];
4161 struct userData *uData;
4163 if(IsService(mn->user))
4166 uData = GetChannelAccess(cData, mn->user->handle_info);
4167 if(!cData->lvlOpts[lvlGiveOps]
4168 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4170 if(!(mn->modes & MODE_CHANOP))
4172 changes->args[used].mode = MODE_CHANOP;
4173 changes->args[used++].u.member = mn;
4176 else if(!cData->lvlOpts[lvlGiveVoice]
4177 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4179 if(mn->modes & MODE_CHANOP)
4181 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4182 changes->args[used++].u.member = mn;
4184 if(!(mn->modes & MODE_VOICE))
4186 changes->args[used].mode = MODE_VOICE;
4187 changes->args[used++].u.member = mn;
4194 changes->args[used].mode = MODE_REMOVE | mn->modes;
4195 changes->args[used++].u.member = mn;
4199 changes->argc = used;
4200 modcmd_chanmode_announce(changes);
4201 mod_chanmode_free(changes);
4202 reply("CSMSG_RESYNCED_USERS", channel->name);
4206 static CHANSERV_FUNC(cmd_seen)
4208 struct userData *uData;
4209 struct handle_info *handle;
4210 char seen[INTERVALLEN];
4214 if(!irccasecmp(argv[1], chanserv->nick))
4216 reply("CSMSG_IS_CHANSERV");
4220 if(!(handle = get_handle_info(argv[1])))
4222 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4226 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4228 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4233 reply("CSMSG_USER_PRESENT", handle->handle);
4234 else if(uData->seen)
4235 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4237 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4239 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4240 reply("CSMSG_USER_VACATION", handle->handle);
4245 static MODCMD_FUNC(cmd_names)
4247 struct userNode *targ;
4248 struct userData *targData;
4249 unsigned int ii, pos;
4252 for(ii=pos=0; ii<channel->members.used; ++ii)
4254 targ = channel->members.list[ii]->user;
4255 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4258 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4261 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4265 if(IsUserSuspended(targData))
4267 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4270 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4271 reply("CSMSG_END_NAMES", channel->name);
4276 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4278 switch(ntype->visible_type)
4280 case NOTE_VIS_ALL: return 1;
4281 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4282 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4287 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4289 struct userData *uData;
4291 switch(ntype->set_access_type)
4293 case NOTE_SET_CHANNEL_ACCESS:
4294 if(!user->handle_info)
4296 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4298 return uData->access >= ntype->set_access.min_ulevel;
4299 case NOTE_SET_CHANNEL_SETTER:
4300 return check_user_level(channel, user, lvlSetters, 1, 0);
4301 case NOTE_SET_PRIVILEGED: default:
4302 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4306 static CHANSERV_FUNC(cmd_note)
4308 struct chanData *cData;
4310 struct note_type *ntype;
4312 cData = channel->channel_info;
4315 reply("CSMSG_NOT_REGISTERED", channel->name);
4319 /* If no arguments, show all visible notes for the channel. */
4325 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4327 note = iter_data(it);
4328 if(!note_type_visible_to_user(cData, note->type, user))
4331 reply("CSMSG_NOTELIST_HEADER", channel->name);
4332 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4335 reply("CSMSG_NOTELIST_END", channel->name);
4337 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4339 /* If one argument, show the named note. */
4342 if((note = dict_find(cData->notes, argv[1], NULL))
4343 && note_type_visible_to_user(cData, note->type, user))
4345 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4347 else if((ntype = dict_find(note_types, argv[1], NULL))
4348 && note_type_visible_to_user(NULL, ntype, user))
4350 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4355 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4359 /* Assume they're trying to set a note. */
4363 ntype = dict_find(note_types, argv[1], NULL);
4366 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4369 else if(note_type_settable_by_user(channel, ntype, user))
4371 note_text = unsplit_string(argv+2, argc-2, NULL);
4372 if((note = dict_find(cData->notes, argv[1], NULL)))
4373 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4374 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4375 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4377 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4379 /* The note is viewable to staff only, so return 0
4380 to keep the invocation from getting logged (or
4381 regular users can see it in !events). */
4387 reply("CSMSG_NO_ACCESS");
4394 static CHANSERV_FUNC(cmd_delnote)
4399 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4400 || !note_type_settable_by_user(channel, note->type, user))
4402 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4405 dict_remove(channel->channel_info->notes, note->type->name);
4406 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4410 static CHANSERV_FUNC(cmd_events)
4412 struct logSearch discrim;
4413 struct logReport report;
4414 unsigned int matches, limit;
4416 limit = (argc > 1) ? atoi(argv[1]) : 10;
4417 if(limit < 1 || limit > 200)
4420 memset(&discrim, 0, sizeof(discrim));
4421 discrim.masks.bot = chanserv;
4422 discrim.masks.channel_name = channel->name;
4424 discrim.masks.command = argv[2];
4425 discrim.limit = limit;
4426 discrim.max_time = INT_MAX;
4427 discrim.severities = 1 << LOG_COMMAND;
4428 report.reporter = chanserv;
4430 reply("CSMSG_EVENT_SEARCH_RESULTS");
4431 matches = log_entry_search(&discrim, log_report_entry, &report);
4433 reply("MSG_MATCH_COUNT", matches);
4435 reply("MSG_NO_MATCHES");
4439 static CHANSERV_FUNC(cmd_say)
4445 msg = unsplit_string(argv + 1, argc - 1, NULL);
4446 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4448 else if(GetUserH(argv[1]))
4451 msg = unsplit_string(argv + 2, argc - 2, NULL);
4452 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4456 reply("MSG_NOT_TARGET_NAME");
4462 static CHANSERV_FUNC(cmd_emote)
4468 /* CTCP is so annoying. */
4469 msg = unsplit_string(argv + 1, argc - 1, NULL);
4470 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4472 else if(GetUserH(argv[1]))
4474 msg = unsplit_string(argv + 2, argc - 2, NULL);
4475 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4479 reply("MSG_NOT_TARGET_NAME");
4485 struct channelList *
4486 chanserv_support_channels(void)
4488 return &chanserv_conf.support_channels;
4491 static CHANSERV_FUNC(cmd_expire)
4493 int channel_count = registered_channels;
4494 expire_channels(NULL);
4495 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4500 chanserv_expire_suspension(void *data)
4502 struct suspended *suspended = data;
4503 struct chanNode *channel;
4505 if(!suspended->expires || (now < suspended->expires))
4506 suspended->revoked = now;
4507 channel = suspended->cData->channel;
4508 suspended->cData->channel = channel;
4509 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4510 if(!IsOffChannel(suspended->cData))
4512 struct mod_chanmode change;
4513 mod_chanmode_init(&change);
4515 change.args[0].mode = MODE_CHANOP;
4516 change.args[0].u.member = AddChannelUser(chanserv, channel);
4517 mod_chanmode_announce(chanserv, channel, &change);
4521 static CHANSERV_FUNC(cmd_csuspend)
4523 struct suspended *suspended;
4524 char reason[MAXLEN];
4525 time_t expiry, duration;
4526 struct userData *uData;
4530 if(IsProtected(channel->channel_info))
4532 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4536 if(argv[1][0] == '!')
4538 else if(IsSuspended(channel->channel_info))
4540 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4541 show_suspension_info(cmd, user, channel->channel_info->suspended);
4545 if(!strcmp(argv[1], "0"))
4547 else if((duration = ParseInterval(argv[1])))
4548 expiry = now + duration;
4551 reply("MSG_INVALID_DURATION", argv[1]);
4555 unsplit_string(argv + 2, argc - 2, reason);
4557 suspended = calloc(1, sizeof(*suspended));
4558 suspended->revoked = 0;
4559 suspended->issued = now;
4560 suspended->suspender = strdup(user->handle_info->handle);
4561 suspended->expires = expiry;
4562 suspended->reason = strdup(reason);
4563 suspended->cData = channel->channel_info;
4564 suspended->previous = suspended->cData->suspended;
4565 suspended->cData->suspended = suspended;
4567 if(suspended->expires)
4568 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4570 if(IsSuspended(channel->channel_info))
4572 suspended->previous->revoked = now;
4573 if(suspended->previous->expires)
4574 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4575 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4576 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4580 /* Mark all users in channel as absent. */
4581 for(uData = channel->channel_info->users; uData; uData = uData->next)
4590 /* Mark the channel as suspended, then part. */
4591 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4592 DelChannelUser(chanserv, channel, suspended->reason, 0);
4593 reply("CSMSG_SUSPENDED", channel->name);
4594 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4595 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4600 static CHANSERV_FUNC(cmd_cunsuspend)
4602 struct suspended *suspended;
4603 char message[MAXLEN];
4605 if(!IsSuspended(channel->channel_info))
4607 reply("CSMSG_NOT_SUSPENDED", channel->name);
4611 suspended = channel->channel_info->suspended;
4613 /* Expire the suspension and join ChanServ to the channel. */
4614 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4615 chanserv_expire_suspension(suspended);
4616 reply("CSMSG_UNSUSPENDED", channel->name);
4617 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4618 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4622 typedef struct chanservSearch
4630 unsigned long flags;
4634 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4637 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4642 search = malloc(sizeof(struct chanservSearch));
4643 memset(search, 0, sizeof(*search));
4646 for(i = 0; i < argc; i++)
4648 /* Assume all criteria require arguments. */
4651 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4655 if(!irccasecmp(argv[i], "name"))
4656 search->name = argv[++i];
4657 else if(!irccasecmp(argv[i], "registrar"))
4658 search->registrar = argv[++i];
4659 else if(!irccasecmp(argv[i], "unvisited"))
4660 search->unvisited = ParseInterval(argv[++i]);
4661 else if(!irccasecmp(argv[i], "registered"))
4662 search->registered = ParseInterval(argv[++i]);
4663 else if(!irccasecmp(argv[i], "flags"))
4666 if(!irccasecmp(argv[i], "nodelete"))
4667 search->flags |= CHANNEL_NODELETE;
4668 else if(!irccasecmp(argv[i], "suspended"))
4669 search->flags |= CHANNEL_SUSPENDED;
4672 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4676 else if(!irccasecmp(argv[i], "limit"))
4677 search->limit = strtoul(argv[++i], NULL, 10);
4680 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4685 if(search->name && !strcmp(search->name, "*"))
4687 if(search->registrar && !strcmp(search->registrar, "*"))
4688 search->registrar = 0;
4697 chanserv_channel_match(struct chanData *channel, search_t search)
4699 const char *name = channel->channel->name;
4700 if((search->name && !match_ircglob(name, search->name)) ||
4701 (search->registrar && !channel->registrar) ||
4702 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4703 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4704 (search->registered && (now - channel->registered) > search->registered) ||
4705 (search->flags && ((search->flags & channel->flags) != search->flags)))
4712 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4714 struct chanData *channel;
4715 unsigned int matches = 0;
4717 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4719 if(!chanserv_channel_match(channel, search))
4729 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4734 search_print(struct chanData *channel, void *data)
4736 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4739 static CHANSERV_FUNC(cmd_search)
4742 unsigned int matches;
4743 channel_search_func action;
4747 if(!irccasecmp(argv[1], "count"))
4748 action = search_count;
4749 else if(!irccasecmp(argv[1], "print"))
4750 action = search_print;
4753 reply("CSMSG_ACTION_INVALID", argv[1]);
4757 search = chanserv_search_create(user, argc - 2, argv + 2);
4761 if(action == search_count)
4762 search->limit = INT_MAX;
4764 if(action == search_print)
4765 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4767 matches = chanserv_channel_search(search, action, user);
4770 reply("MSG_MATCH_COUNT", matches);
4772 reply("MSG_NO_MATCHES");
4778 static CHANSERV_FUNC(cmd_unvisited)
4780 struct chanData *cData;
4781 time_t interval = chanserv_conf.channel_expire_delay;
4782 char buffer[INTERVALLEN];
4783 unsigned int limit = 25, matches = 0;
4787 interval = ParseInterval(argv[1]);
4789 limit = atoi(argv[2]);
4792 intervalString(buffer, interval, user->handle_info);
4793 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4795 for(cData = channelList; cData && matches < limit; cData = cData->next)
4797 if((now - cData->visited) < interval)
4800 intervalString(buffer, now - cData->visited, user->handle_info);
4801 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4808 static MODCMD_FUNC(chan_opt_defaulttopic)
4814 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4816 reply("CSMSG_TOPIC_LOCKED", channel->name);
4820 topic = unsplit_string(argv+1, argc-1, NULL);
4822 free(channel->channel_info->topic);
4823 if(topic[0] == '*' && topic[1] == 0)
4825 topic = channel->channel_info->topic = NULL;
4829 topic = channel->channel_info->topic = strdup(topic);
4830 if(channel->channel_info->topic_mask
4831 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4832 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4834 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4837 if(channel->channel_info->topic)
4838 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4840 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4844 static MODCMD_FUNC(chan_opt_topicmask)
4848 struct chanData *cData = channel->channel_info;
4851 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4853 reply("CSMSG_TOPIC_LOCKED", channel->name);
4857 mask = unsplit_string(argv+1, argc-1, NULL);
4859 if(cData->topic_mask)
4860 free(cData->topic_mask);
4861 if(mask[0] == '*' && mask[1] == 0)
4863 cData->topic_mask = 0;
4867 cData->topic_mask = strdup(mask);
4869 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4870 else if(!match_ircglob(cData->topic, cData->topic_mask))
4871 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4875 if(channel->channel_info->topic_mask)
4876 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4878 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4882 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4886 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4890 if(greeting[0] == '*' && greeting[1] == 0)
4894 unsigned int length = strlen(greeting);
4895 if(length > chanserv_conf.greeting_length)
4897 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4900 *data = strdup(greeting);
4909 reply(name, user_find_message(user, "MSG_NONE"));
4913 static MODCMD_FUNC(chan_opt_greeting)
4915 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4918 static MODCMD_FUNC(chan_opt_usergreeting)
4920 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4923 static MODCMD_FUNC(chan_opt_modes)
4925 struct mod_chanmode *new_modes;
4926 char modes[MODELEN];
4930 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4932 reply("CSMSG_NO_ACCESS");
4935 if(argv[1][0] == '*' && argv[1][1] == 0)
4937 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4939 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4941 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4944 else if(new_modes->argc > 1)
4946 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4947 mod_chanmode_free(new_modes);
4952 channel->channel_info->modes = *new_modes;
4953 modcmd_chanmode_announce(new_modes);
4954 mod_chanmode_free(new_modes);
4958 mod_chanmode_format(&channel->channel_info->modes, modes);
4960 reply("CSMSG_SET_MODES", modes);
4962 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4966 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4968 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4970 struct chanData *cData = channel->channel_info;
4975 /* Set flag according to value. */
4976 if(enabled_string(argv[1]))
4978 cData->flags |= mask;
4981 else if(disabled_string(argv[1]))
4983 cData->flags &= ~mask;
4988 reply("MSG_INVALID_BINARY", argv[1]);
4994 /* Find current option value. */
4995 value = (cData->flags & mask) ? 1 : 0;
4999 reply(name, user_find_message(user, "MSG_ON"));
5001 reply(name, user_find_message(user, "MSG_OFF"));
5005 static MODCMD_FUNC(chan_opt_nodelete)
5007 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5009 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5013 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5016 static MODCMD_FUNC(chan_opt_dynlimit)
5018 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5021 static MODCMD_FUNC(chan_opt_offchannel)
5023 struct chanData *cData = channel->channel_info;
5028 /* Set flag according to value. */
5029 if(enabled_string(argv[1]))
5031 if(!IsOffChannel(cData))
5032 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5033 cData->flags |= CHANNEL_OFFCHANNEL;
5036 else if(disabled_string(argv[1]))
5038 if(IsOffChannel(cData))
5040 struct mod_chanmode change;
5041 mod_chanmode_init(&change);
5043 change.args[0].mode = MODE_CHANOP;
5044 change.args[0].u.member = AddChannelUser(chanserv, channel);
5045 mod_chanmode_announce(chanserv, channel, &change);
5047 cData->flags &= ~CHANNEL_OFFCHANNEL;
5052 reply("MSG_INVALID_BINARY", argv[1]);
5058 /* Find current option value. */
5059 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5063 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5065 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5069 static MODCMD_FUNC(chan_opt_defaults)
5071 struct userData *uData;
5072 struct chanData *cData;
5073 const char *confirm;
5074 enum levelOption lvlOpt;
5075 enum charOption chOpt;
5077 cData = channel->channel_info;
5078 uData = GetChannelUser(cData, user->handle_info);
5079 if(!uData || (uData->access < UL_OWNER))
5081 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5084 confirm = make_confirmation_string(uData);
5085 if((argc < 2) || strcmp(argv[1], confirm))
5087 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5090 cData->flags = CHANNEL_DEFAULT_FLAGS;
5091 cData->modes = chanserv_conf.default_modes;
5092 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5093 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5094 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5095 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5096 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5101 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5103 struct chanData *cData = channel->channel_info;
5104 struct userData *uData;
5105 unsigned short value;
5109 if(!check_user_level(channel, user, option, 1, 1))
5111 reply("CSMSG_CANNOT_SET");
5114 value = user_level_from_name(argv[1], UL_OWNER+1);
5115 if(!value && strcmp(argv[1], "0"))
5117 reply("CSMSG_INVALID_ACCESS", argv[1]);
5120 uData = GetChannelUser(cData, user->handle_info);
5121 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5123 reply("CSMSG_BAD_SETLEVEL");
5129 if(value > cData->lvlOpts[lvlGiveOps])
5131 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5136 if(value < cData->lvlOpts[lvlGiveVoice])
5138 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5143 /* This test only applies to owners, since non-owners
5144 * trying to set an option to above their level get caught
5145 * by the CSMSG_BAD_SETLEVEL test above.
5147 if(value > uData->access)
5149 reply("CSMSG_BAD_SETTERS");
5156 cData->lvlOpts[option] = value;
5158 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5162 static MODCMD_FUNC(chan_opt_enfops)
5164 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5167 static MODCMD_FUNC(chan_opt_giveops)
5169 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5172 static MODCMD_FUNC(chan_opt_enfmodes)
5174 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5177 static MODCMD_FUNC(chan_opt_enftopic)
5179 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5182 static MODCMD_FUNC(chan_opt_pubcmd)
5184 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5187 static MODCMD_FUNC(chan_opt_setters)
5189 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5192 static MODCMD_FUNC(chan_opt_ctcpusers)
5194 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5197 static MODCMD_FUNC(chan_opt_userinfo)
5199 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5202 static MODCMD_FUNC(chan_opt_givevoice)
5204 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5207 static MODCMD_FUNC(chan_opt_topicsnarf)
5209 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5212 static MODCMD_FUNC(chan_opt_inviteme)
5214 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5218 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5220 struct chanData *cData = channel->channel_info;
5221 int count = charOptions[option].count, index;
5225 index = atoi(argv[1]);
5227 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5229 reply("CSMSG_INVALID_NUMERIC", index);
5230 /* Show possible values. */
5231 for(index = 0; index < count; index++)
5232 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5236 cData->chOpts[option] = charOptions[option].values[index].value;
5240 /* Find current option value. */
5243 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5247 /* Somehow, the option value is corrupt; reset it to the default. */
5248 cData->chOpts[option] = charOptions[option].default_value;
5253 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5257 static MODCMD_FUNC(chan_opt_protect)
5259 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5262 static MODCMD_FUNC(chan_opt_toys)
5264 return channel_multiple_option(chToys, CSFUNC_ARGS);
5267 static MODCMD_FUNC(chan_opt_ctcpreaction)
5269 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5272 static MODCMD_FUNC(chan_opt_topicrefresh)
5274 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5277 static struct svccmd_list set_shows_list;
5280 handle_svccmd_unbind(struct svccmd *target) {
5282 for(ii=0; ii<set_shows_list.used; ++ii)
5283 if(target == set_shows_list.list[ii])
5284 set_shows_list.used = 0;
5287 static CHANSERV_FUNC(cmd_set)
5289 struct svccmd *subcmd;
5293 /* Check if we need to (re-)initialize set_shows_list. */
5294 if(!set_shows_list.used)
5296 if(!set_shows_list.size)
5298 set_shows_list.size = chanserv_conf.set_shows->used;
5299 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5301 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5303 const char *name = chanserv_conf.set_shows->list[ii];
5304 sprintf(buf, "%s %s", argv[0], name);
5305 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5308 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5311 svccmd_list_append(&set_shows_list, subcmd);
5317 reply("CSMSG_CHANNEL_OPTIONS");
5318 for(ii = 0; ii < set_shows_list.used; ii++)
5320 subcmd = set_shows_list.list[ii];
5321 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5326 sprintf(buf, "%s %s", argv[0], argv[1]);
5327 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5330 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5333 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5335 reply("CSMSG_NO_ACCESS");
5339 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5343 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5345 struct userData *uData;
5347 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5350 reply("CSMSG_NOT_USER", channel->name);
5356 /* Just show current option value. */
5358 else if(enabled_string(argv[1]))
5360 uData->flags |= mask;
5362 else if(disabled_string(argv[1]))
5364 uData->flags &= ~mask;
5368 reply("MSG_INVALID_BINARY", argv[1]);
5372 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5376 static MODCMD_FUNC(user_opt_noautoop)
5378 struct userData *uData;
5380 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5383 reply("CSMSG_NOT_USER", channel->name);
5386 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5387 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5389 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5392 static MODCMD_FUNC(user_opt_autoinvite)
5394 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5397 static MODCMD_FUNC(user_opt_info)
5399 struct userData *uData;
5402 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5406 /* If they got past the command restrictions (which require access)
5407 * but fail this test, we have some fool with security override on.
5409 reply("CSMSG_NOT_USER", channel->name);
5416 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5417 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5419 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5422 bp = strcspn(infoline, "\001");
5425 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5430 if(infoline[0] == '*' && infoline[1] == 0)
5433 uData->info = strdup(infoline);
5436 reply("CSMSG_USET_INFO", uData->info);
5438 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5442 struct svccmd_list uset_shows_list;
5444 static CHANSERV_FUNC(cmd_uset)
5446 struct svccmd *subcmd;
5450 /* Check if we need to (re-)initialize uset_shows_list. */
5451 if(!uset_shows_list.used)
5455 "NoAutoOp", "AutoInvite", "Info"
5458 if(!uset_shows_list.size)
5460 uset_shows_list.size = ArrayLength(options);
5461 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5463 for(ii = 0; ii < ArrayLength(options); ii++)
5465 const char *name = options[ii];
5466 sprintf(buf, "%s %s", argv[0], name);
5467 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5470 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5473 svccmd_list_append(&uset_shows_list, subcmd);
5479 /* Do this so options are presented in a consistent order. */
5480 reply("CSMSG_USER_OPTIONS");
5481 for(ii = 0; ii < uset_shows_list.used; ii++)
5482 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5486 sprintf(buf, "%s %s", argv[0], argv[1]);
5487 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5490 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5494 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5497 static CHANSERV_FUNC(cmd_giveownership)
5499 struct handle_info *new_owner_hi;
5500 struct userData *new_owner;
5501 struct userData *curr_user;
5502 struct userData *invoker;
5503 struct chanData *cData = channel->channel_info;
5504 struct do_not_register *dnr;
5505 const char *confirm;
5507 unsigned short co_access;
5508 char reason[MAXLEN];
5511 curr_user = GetChannelAccess(cData, user->handle_info);
5512 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5513 if(!curr_user || (curr_user->access != UL_OWNER))
5515 struct userData *owner = NULL;
5516 for(curr_user = channel->channel_info->users;
5518 curr_user = curr_user->next)
5520 if(curr_user->access != UL_OWNER)
5524 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5531 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5533 char delay[INTERVALLEN];
5534 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5535 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5538 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5540 if(new_owner_hi == user->handle_info)
5542 reply("CSMSG_NO_TRANSFER_SELF");
5545 new_owner = GetChannelAccess(cData, new_owner_hi);
5550 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5554 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5558 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5560 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5563 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5564 if(!IsHelping(user))
5565 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5567 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5570 invoker = GetChannelUser(cData, user->handle_info);
5571 if(invoker->access <= UL_OWNER)
5573 confirm = make_confirmation_string(curr_user);
5574 if((argc < 3) || strcmp(argv[2], confirm))
5576 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5580 if(new_owner->access >= UL_COOWNER)
5581 co_access = new_owner->access;
5583 co_access = UL_COOWNER;
5584 new_owner->access = UL_OWNER;
5586 curr_user->access = co_access;
5587 cData->ownerTransfer = now;
5588 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5589 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5590 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5594 static CHANSERV_FUNC(cmd_suspend)
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_ALREADY_SUSPENDED", hi->handle);
5619 target->present = 0;
5622 target->flags |= USER_SUSPENDED;
5623 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5627 static CHANSERV_FUNC(cmd_unsuspend)
5629 struct handle_info *hi;
5630 struct userData *self, *target;
5633 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5634 self = GetChannelUser(channel->channel_info, user->handle_info);
5635 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5637 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5640 if(target->access >= self->access)
5642 reply("MSG_USER_OUTRANKED", hi->handle);
5645 if(!(target->flags & USER_SUSPENDED))
5647 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5650 target->flags &= ~USER_SUSPENDED;
5651 scan_user_presence(target, NULL);
5652 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5656 static MODCMD_FUNC(cmd_deleteme)
5658 struct handle_info *hi;
5659 struct userData *target;
5660 const char *confirm_string;
5661 unsigned short access;
5664 hi = user->handle_info;
5665 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5667 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5670 if(target->access == UL_OWNER)
5672 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5675 confirm_string = make_confirmation_string(target);
5676 if((argc < 2) || strcmp(argv[1], confirm_string))
5678 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5681 access = target->access;
5682 channel_name = strdup(channel->name);
5683 del_channel_user(target, 1);
5684 reply("CSMSG_DELETED_YOU", access, channel_name);
5690 chanserv_refresh_topics(UNUSED_ARG(void *data))
5692 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5693 struct chanData *cData;
5696 for(cData = channelList; cData; cData = cData->next)
5698 if(IsSuspended(cData))
5700 opt = cData->chOpts[chTopicRefresh];
5703 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5706 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5707 cData->last_refresh = refresh_num;
5709 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5712 static CHANSERV_FUNC(cmd_unf)
5716 char response[MAXLEN];
5717 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5718 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5719 irc_privmsg(cmd->parent->bot, channel->name, response);
5722 reply("CSMSG_UNF_RESPONSE");
5726 static CHANSERV_FUNC(cmd_ping)
5730 char response[MAXLEN];
5731 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5732 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5733 irc_privmsg(cmd->parent->bot, channel->name, response);
5736 reply("CSMSG_PING_RESPONSE");
5740 static CHANSERV_FUNC(cmd_wut)
5744 char response[MAXLEN];
5745 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5746 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5747 irc_privmsg(cmd->parent->bot, channel->name, response);
5750 reply("CSMSG_WUT_RESPONSE");
5754 static CHANSERV_FUNC(cmd_8ball)
5756 unsigned int i, j, accum;
5761 for(i=1; i<argc; i++)
5762 for(j=0; argv[i][j]; j++)
5763 accum = (accum << 5) - accum + toupper(argv[i][j]);
5764 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5767 char response[MAXLEN];
5768 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5769 irc_privmsg(cmd->parent->bot, channel->name, response);
5772 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5776 static CHANSERV_FUNC(cmd_d)
5778 unsigned long sides, count, modifier, ii, total;
5779 char response[MAXLEN], *sep;
5783 if((count = strtoul(argv[1], &sep, 10)) < 1)
5793 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5794 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5798 else if((sep[0] == '-') && isdigit(sep[1]))
5799 modifier = strtoul(sep, NULL, 10);
5800 else if((sep[0] == '+') && isdigit(sep[1]))
5801 modifier = strtoul(sep+1, NULL, 10);
5808 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5813 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5816 for(total = ii = 0; ii < count; ++ii)
5817 total += (rand() % sides) + 1;
5820 if((count > 1) || modifier)
5822 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5823 sprintf(response, fmt, total, count, sides, modifier);
5827 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5828 sprintf(response, fmt, total, sides);
5831 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5833 send_message_type(4, user, cmd->parent->bot, "%s", response);
5837 static CHANSERV_FUNC(cmd_huggle)
5839 /* CTCP must be via PRIVMSG, never notice */
5841 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5843 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5848 chanserv_adjust_limit(void *data)
5850 struct mod_chanmode change;
5851 struct chanData *cData = data;
5852 struct chanNode *channel = cData->channel;
5855 if(IsSuspended(cData))
5858 cData->limitAdjusted = now;
5859 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5860 if(cData->modes.modes_set & MODE_LIMIT)
5862 if(limit > cData->modes.new_limit)
5863 limit = cData->modes.new_limit;
5864 else if(limit == cData->modes.new_limit)
5868 mod_chanmode_init(&change);
5869 change.modes_set = MODE_LIMIT;
5870 change.new_limit = limit;
5871 mod_chanmode_announce(chanserv, channel, &change);
5875 handle_new_channel(struct chanNode *channel)
5877 struct chanData *cData;
5879 if(!(cData = channel->channel_info))
5882 if(cData->modes.modes_set || cData->modes.modes_clear)
5883 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5885 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5886 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5889 /* Welcome to my worst nightmare. Warning: Read (or modify)
5890 the code below at your own risk. */
5892 handle_join(struct modeNode *mNode)
5894 struct mod_chanmode change;
5895 struct userNode *user = mNode->user;
5896 struct chanNode *channel = mNode->channel;
5897 struct chanData *cData;
5898 struct userData *uData = NULL;
5899 struct banData *bData;
5900 struct handle_info *handle;
5901 unsigned int modes = 0, info = 0;
5904 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5907 cData = channel->channel_info;
5908 if(channel->members.used > cData->max)
5909 cData->max = channel->members.used;
5911 /* Check for bans. If they're joining through a ban, one of two
5913 * 1: Join during a netburst, by riding the break. Kick them
5914 * unless they have ops or voice in the channel.
5915 * 2: They're allowed to join through the ban (an invite in
5916 * ircu2.10, or a +e on Hybrid, or something).
5917 * If they're not joining through a ban, and the banlist is not
5918 * full, see if they're on the banlist for the channel. If so,
5921 if(user->uplink->burst && !mNode->modes)
5924 for(ii = 0; ii < channel->banlist.used; ii++)
5926 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5928 /* Riding a netburst. Naughty. */
5929 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5935 mod_chanmode_init(&change);
5937 if(channel->banlist.used < MAXBANS)
5939 /* Not joining through a ban. */
5940 for(bData = cData->bans;
5941 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5942 bData = bData->next);
5946 char kick_reason[MAXLEN];
5947 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5949 bData->triggered = now;
5950 if(bData != cData->bans)
5952 /* Shuffle the ban to the head of the list. */
5954 bData->next->prev = bData->prev;
5956 bData->prev->next = bData->next;
5959 bData->next = cData->bans;
5962 cData->bans->prev = bData;
5963 cData->bans = bData;
5966 change.args[0].mode = MODE_BAN;
5967 change.args[0].u.hostmask = bData->mask;
5968 mod_chanmode_announce(chanserv, channel, &change);
5969 KickChannelUser(user, channel, chanserv, kick_reason);
5974 /* ChanServ will not modify the limits in join-flooded channels.
5975 It will also skip DynLimit processing when the user (or srvx)
5976 is bursting in, because there are likely more incoming. */
5977 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5978 && !user->uplink->burst
5979 && !channel->join_flooded
5980 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5982 /* The user count has begun "bumping" into the channel limit,
5983 so set a timer to raise the limit a bit. Any previous
5984 timers are removed so three incoming users within the delay
5985 results in one limit change, not three. */
5987 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5988 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5991 if(channel->join_flooded)
5993 /* don't automatically give ops or voice during a join flood */
5995 else if(cData->lvlOpts[lvlGiveOps] == 0)
5996 modes |= MODE_CHANOP;
5997 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5998 modes |= MODE_VOICE;
6000 greeting = cData->greeting;
6001 if(user->handle_info)
6003 handle = user->handle_info;
6005 if(IsHelper(user) && !IsHelping(user))
6008 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6010 if(channel == chanserv_conf.support_channels.list[ii])
6012 HANDLE_SET_FLAG(user->handle_info, HELPING);
6018 uData = GetTrueChannelAccess(cData, handle);
6019 if(uData && !IsUserSuspended(uData))
6021 /* Ops and above were handled by the above case. */
6022 if(IsUserAutoOp(uData))
6024 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6025 modes |= MODE_CHANOP;
6026 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6027 modes |= MODE_VOICE;
6029 if(uData->access >= UL_PRESENT)
6030 cData->visited = now;
6031 if(cData->user_greeting)
6032 greeting = cData->user_greeting;
6034 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6035 && ((now - uData->seen) >= chanserv_conf.info_delay)
6043 /* If user joining normally (not during burst), apply op or voice,
6044 * and send greeting/userinfo as appropriate.
6046 if(!user->uplink->burst)
6050 if(modes & MODE_CHANOP)
6051 modes &= ~MODE_VOICE;
6052 change.args[0].mode = modes;
6053 change.args[0].u.member = mNode;
6054 mod_chanmode_announce(chanserv, channel, &change);
6057 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6059 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6065 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6067 struct mod_chanmode change;
6068 struct userData *channel;
6069 unsigned int ii, jj;
6071 if(!user->handle_info)
6074 mod_chanmode_init(&change);
6076 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6078 struct chanNode *cn;
6079 struct modeNode *mn;
6080 if(IsUserSuspended(channel)
6081 || IsSuspended(channel->channel)
6082 || !(cn = channel->channel->channel))
6085 mn = GetUserMode(cn, user);
6088 if(!IsUserSuspended(channel)
6089 && IsUserAutoInvite(channel)
6090 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6092 && !user->uplink->burst)
6093 irc_invite(chanserv, user, cn);
6097 if(channel->access >= UL_PRESENT)
6098 channel->channel->visited = now;
6100 if(IsUserAutoOp(channel))
6102 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6103 change.args[0].mode = MODE_CHANOP;
6104 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6105 change.args[0].mode = MODE_VOICE;
6107 change.args[0].mode = 0;
6108 change.args[0].u.member = mn;
6109 if(change.args[0].mode)
6110 mod_chanmode_announce(chanserv, cn, &change);
6113 channel->seen = now;
6114 channel->present = 1;
6117 for(ii = 0; ii < user->channels.used; ++ii)
6119 struct chanNode *channel = user->channels.list[ii]->channel;
6120 struct banData *ban;
6122 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6123 || !channel->channel_info
6124 || IsSuspended(channel->channel_info))
6126 for(jj = 0; jj < channel->banlist.used; ++jj)
6127 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6129 if(jj < channel->banlist.used)
6131 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6133 char kick_reason[MAXLEN];
6134 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6136 change.args[0].mode = MODE_BAN;
6137 change.args[0].u.hostmask = ban->mask;
6138 mod_chanmode_announce(chanserv, channel, &change);
6139 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6140 KickChannelUser(user, channel, chanserv, kick_reason);
6141 ban->triggered = now;
6146 if(IsSupportHelper(user))
6148 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6150 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6152 HANDLE_SET_FLAG(user->handle_info, HELPING);
6160 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6162 struct chanData *cData;
6163 struct userData *uData;
6165 cData = mn->channel->channel_info;
6166 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6169 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6171 /* Allow for a bit of padding so that the limit doesn't
6172 track the user count exactly, which could get annoying. */
6173 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6175 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6176 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6180 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6182 scan_user_presence(uData, mn->user);
6184 if (uData->access >= UL_PRESENT)
6185 cData->visited = now;
6188 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6190 unsigned int ii, jj;
6191 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6193 for(jj = 0; jj < mn->user->channels.used; ++jj)
6194 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6196 if(jj < mn->user->channels.used)
6199 if(ii == chanserv_conf.support_channels.used)
6200 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6205 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6207 struct userData *uData;
6209 if(!channel->channel_info || !kicker || IsService(kicker)
6210 || (kicker == victim) || IsSuspended(channel->channel_info)
6211 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6214 if(protect_user(victim, kicker, channel->channel_info))
6216 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6217 KickChannelUser(kicker, channel, chanserv, reason);
6220 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6225 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6227 struct chanData *cData;
6229 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6232 cData = channel->channel_info;
6233 if(bad_topic(channel, user, channel->topic))
6235 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6236 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6237 SetChannelTopic(channel, chanserv, old_topic, 1);
6238 else if(cData->topic)
6239 SetChannelTopic(channel, chanserv, cData->topic, 1);
6242 /* With topicsnarf, grab the topic and save it as the default topic. */
6243 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6246 cData->topic = strdup(channel->topic);
6252 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6254 struct mod_chanmode *bounce = NULL;
6255 unsigned int bnc, ii;
6258 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6261 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6262 && mode_lock_violated(&channel->channel_info->modes, change))
6264 char correct[MAXLEN];
6265 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6266 mod_chanmode_format(&channel->channel_info->modes, correct);
6267 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6269 for(ii = bnc = 0; ii < change->argc; ++ii)
6271 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6273 const struct userNode *victim = change->args[ii].u.member->user;
6274 if(!protect_user(victim, user, channel->channel_info))
6277 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6280 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6281 bounce->args[bnc].u.member = GetUserMode(channel, user);
6282 if(bounce->args[bnc].u.member)
6286 bounce->args[bnc].mode = MODE_CHANOP;
6287 bounce->args[bnc].u.member = change->args[ii].u.member;
6289 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6291 else if(change->args[ii].mode & MODE_CHANOP)
6293 const struct userNode *victim = change->args[ii].u.member->user;
6294 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6297 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6298 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6299 bounce->args[bnc].u.member = change->args[ii].u.member;
6302 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6304 const char *ban = change->args[ii].u.hostmask;
6305 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6308 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6309 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6310 bounce->args[bnc].u.hostmask = strdup(ban);
6312 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6317 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6318 mod_chanmode_announce(chanserv, channel, bounce);
6319 for(ii = 0; ii < change->argc; ++ii)
6320 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6321 free((char*)bounce->args[ii].u.hostmask);
6322 mod_chanmode_free(bounce);
6327 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6329 struct chanNode *channel;
6330 struct banData *bData;
6331 struct mod_chanmode change;
6332 unsigned int ii, jj;
6333 char kick_reason[MAXLEN];
6335 mod_chanmode_init(&change);
6337 change.args[0].mode = MODE_BAN;
6338 for(ii = 0; ii < user->channels.used; ++ii)
6340 channel = user->channels.list[ii]->channel;
6341 /* Need not check for bans if they're opped or voiced. */
6342 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6344 /* Need not check for bans unless channel registration is active. */
6345 if(!channel->channel_info || IsSuspended(channel->channel_info))
6347 /* Look for a matching ban already on the channel. */
6348 for(jj = 0; jj < channel->banlist.used; ++jj)
6349 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6351 /* Need not act if we found one. */
6352 if(jj < channel->banlist.used)
6354 /* Look for a matching ban in this channel. */
6355 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6357 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6359 change.args[0].u.hostmask = bData->mask;
6360 mod_chanmode_announce(chanserv, channel, &change);
6361 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6362 KickChannelUser(user, channel, chanserv, kick_reason);
6363 bData->triggered = now;
6364 break; /* we don't need to check any more bans in the channel */
6369 static void handle_rename(struct handle_info *handle, const char *old_handle)
6371 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6375 dict_remove2(handle_dnrs, old_handle, 1);
6376 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6377 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6382 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6384 struct userNode *h_user;
6386 if(handle->channels)
6388 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6389 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6391 while(handle->channels)
6392 del_channel_user(handle->channels, 1);
6397 handle_server_link(UNUSED_ARG(struct server *server))
6399 struct chanData *cData;
6401 for(cData = channelList; cData; cData = cData->next)
6403 if(!IsSuspended(cData))
6404 cData->may_opchan = 1;
6405 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6406 && !cData->channel->join_flooded
6407 && ((cData->channel->limit - cData->channel->members.used)
6408 < chanserv_conf.adjust_threshold))
6410 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6411 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6417 chanserv_conf_read(void)
6421 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6422 struct mod_chanmode *change;
6423 struct string_list *strlist;
6424 struct chanNode *chan;
6427 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6429 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6432 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6433 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6434 chanserv_conf.support_channels.used = 0;
6435 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6437 for(ii = 0; ii < strlist->used; ++ii)
6439 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6442 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6444 channelList_append(&chanserv_conf.support_channels, chan);
6447 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6450 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6453 chan = AddChannel(str, now, str2, NULL);
6455 channelList_append(&chanserv_conf.support_channels, chan);
6457 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6458 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6459 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6460 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6461 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6462 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6463 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6464 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6465 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6466 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6467 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6468 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6469 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6470 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6471 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6472 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6473 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6474 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6475 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6476 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6477 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6478 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6479 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6481 NickChange(chanserv, str, 0);
6482 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6483 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6484 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6485 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6486 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6487 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6488 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6489 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6490 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6491 chanserv_conf.max_owned = str ? atoi(str) : 5;
6492 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6493 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6494 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6495 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6496 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6497 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6498 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6501 safestrncpy(mode_line, str, sizeof(mode_line));
6502 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6503 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6504 && (change->argc < 2))
6506 chanserv_conf.default_modes = *change;
6507 mod_chanmode_free(change);
6509 free_string_list(chanserv_conf.set_shows);
6510 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6512 strlist = string_list_copy(strlist);
6515 static const char *list[] = {
6516 /* free form text */
6517 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6518 /* options based on user level */
6519 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6520 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6521 /* multiple choice options */
6522 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6523 /* binary options */
6524 "DynLimit", "NoDelete",
6529 strlist = alloc_string_list(ArrayLength(list)-1);
6530 for(ii=0; list[ii]; ii++)
6531 string_list_append(strlist, strdup(list[ii]));
6533 chanserv_conf.set_shows = strlist;
6534 /* We don't look things up now, in case the list refers to options
6535 * defined by modules initialized after this point. Just mark the
6536 * function list as invalid, so it will be initialized.
6538 set_shows_list.used = 0;
6539 free_string_list(chanserv_conf.eightball);
6540 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6543 strlist = string_list_copy(strlist);
6547 strlist = alloc_string_list(4);
6548 string_list_append(strlist, strdup("Yes."));
6549 string_list_append(strlist, strdup("No."));
6550 string_list_append(strlist, strdup("Maybe so."));
6552 chanserv_conf.eightball = strlist;
6553 free_string_list(chanserv_conf.old_ban_names);
6554 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6556 strlist = string_list_copy(strlist);
6558 strlist = alloc_string_list(2);
6559 chanserv_conf.old_ban_names = strlist;
6560 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6561 off_channel = str ? atoi(str) : 0;
6565 chanserv_note_type_read(const char *key, struct record_data *rd)
6568 struct note_type *ntype;
6571 if(!(obj = GET_RECORD_OBJECT(rd)))
6573 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6576 if(!(ntype = chanserv_create_note_type(key)))
6578 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6582 /* Figure out set access */
6583 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6585 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6586 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6588 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6590 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6591 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6593 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6595 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6599 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6600 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6601 ntype->set_access.min_opserv = 0;
6604 /* Figure out visibility */
6605 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6606 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6607 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6608 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6609 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6610 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6611 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6612 ntype->visible_type = NOTE_VIS_ALL;
6614 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6616 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6617 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6621 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6623 struct handle_info *handle;
6624 struct userData *uData;
6625 char *seen, *inf, *flags;
6627 unsigned short access;
6629 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6631 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6635 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6636 if(access > UL_OWNER)
6638 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6642 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6643 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6644 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6645 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6646 handle = get_handle_info(key);
6649 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6653 uData = add_channel_user(chan, handle, access, last_seen, inf);
6654 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6658 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6660 struct banData *bData;
6661 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6662 time_t set_time, triggered_time, expires_time;
6664 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6666 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6670 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6671 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6672 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6673 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6674 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6675 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6676 if (!reason || !owner)
6679 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6680 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6682 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6684 expires_time = set_time + atoi(s_duration);
6688 if(!reason || (expires_time && (expires_time < now)))
6691 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6694 static struct suspended *
6695 chanserv_read_suspended(dict_t obj)
6697 struct suspended *suspended = calloc(1, sizeof(*suspended));
6701 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6702 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6703 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6704 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6705 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6706 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6707 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6708 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6709 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6710 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6715 chanserv_channel_read(const char *key, struct record_data *hir)
6717 struct suspended *suspended;
6718 struct mod_chanmode *modes;
6719 struct chanNode *cNode;
6720 struct chanData *cData;
6721 struct dict *channel, *obj;
6722 char *str, *argv[10];
6726 channel = hir->d.object;
6728 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6731 cNode = AddChannel(key, now, NULL, NULL);
6734 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6737 cData = register_channel(cNode, str);
6740 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6744 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6746 enum levelOption lvlOpt;
6747 enum charOption chOpt;
6749 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6750 cData->flags = atoi(str);
6752 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6754 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6756 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6757 else if(levelOptions[lvlOpt].old_flag)
6759 if(cData->flags & levelOptions[lvlOpt].old_flag)
6760 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6762 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6766 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6768 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6770 cData->chOpts[chOpt] = str[0];
6773 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6775 enum levelOption lvlOpt;
6776 enum charOption chOpt;
6779 cData->flags = base64toint(str, 5);
6780 count = strlen(str += 5);
6781 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6784 if(levelOptions[lvlOpt].old_flag)
6786 if(cData->flags & levelOptions[lvlOpt].old_flag)
6787 lvl = levelOptions[lvlOpt].flag_value;
6789 lvl = levelOptions[lvlOpt].default_value;
6791 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6793 case 'c': lvl = UL_COOWNER; break;
6794 case 'm': lvl = UL_MASTER; break;
6795 case 'n': lvl = UL_OWNER+1; break;
6796 case 'o': lvl = UL_OP; break;
6797 case 'p': lvl = UL_PEON; break;
6798 case 'w': lvl = UL_OWNER; break;
6799 default: lvl = 0; break;
6801 cData->lvlOpts[lvlOpt] = lvl;
6803 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6804 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6807 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6809 suspended = chanserv_read_suspended(obj);
6810 cData->suspended = suspended;
6811 suspended->cData = cData;
6812 /* We could use suspended->expires and suspended->revoked to
6813 * set the CHANNEL_SUSPENDED flag, but we don't. */
6815 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6817 suspended = calloc(1, sizeof(*suspended));
6818 suspended->issued = 0;
6819 suspended->revoked = 0;
6820 suspended->suspender = strdup(str);
6821 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6822 suspended->expires = str ? atoi(str) : 0;
6823 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6824 suspended->reason = strdup(str ? str : "No reason");
6825 suspended->previous = NULL;
6826 cData->suspended = suspended;
6827 suspended->cData = cData;
6831 cData->flags &= ~CHANNEL_SUSPENDED;
6832 suspended = NULL; /* to squelch a warning */
6835 if(IsSuspended(cData)) {
6836 if(suspended->expires > now)
6837 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6838 else if(suspended->expires)
6839 cData->flags &= ~CHANNEL_SUSPENDED;
6842 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6843 struct mod_chanmode change;
6844 mod_chanmode_init(&change);
6846 change.args[0].mode = MODE_CHANOP;
6847 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6848 mod_chanmode_announce(chanserv, cNode, &change);
6851 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6852 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6853 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6854 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6855 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6856 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6857 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6858 cData->max = str ? atoi(str) : 0;
6859 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6860 cData->greeting = str ? strdup(str) : NULL;
6861 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6862 cData->user_greeting = str ? strdup(str) : NULL;
6863 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6864 cData->topic_mask = str ? strdup(str) : NULL;
6865 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6866 cData->topic = str ? strdup(str) : NULL;
6868 if(!IsSuspended(cData)
6869 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6870 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6871 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6872 cData->modes = *modes;
6874 cData->modes.modes_set |= MODE_REGISTERED;
6875 if(cData->modes.argc > 1)
6876 cData->modes.argc = 1;
6877 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6878 mod_chanmode_free(modes);
6881 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6882 for(it = dict_first(obj); it; it = iter_next(it))
6883 user_read_helper(iter_key(it), iter_data(it), cData);
6885 if(!cData->users && !IsProtected(cData))
6887 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6888 unregister_channel(cData, "has empty user list.");
6892 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6893 for(it = dict_first(obj); it; it = iter_next(it))
6894 ban_read_helper(iter_key(it), iter_data(it), cData);
6896 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6897 for(it = dict_first(obj); it; it = iter_next(it))
6899 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6900 struct record_data *rd = iter_data(it);
6901 const char *note, *setter;
6903 if(rd->type != RECDB_OBJECT)
6905 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6909 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6911 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6913 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6917 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6918 if(!setter) setter = "<unknown>";
6919 chanserv_add_channel_note(cData, ntype, setter, note);
6927 chanserv_dnr_read(const char *key, struct record_data *hir)
6929 const char *setter, *reason, *str;
6930 struct do_not_register *dnr;
6932 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6935 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6938 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6941 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6944 dnr = chanserv_add_dnr(key, setter, reason);
6947 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6949 dnr->set = atoi(str);
6955 chanserv_saxdb_read(struct dict *database)
6957 struct dict *section;
6960 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6961 for(it = dict_first(section); it; it = iter_next(it))
6962 chanserv_note_type_read(iter_key(it), iter_data(it));
6964 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6965 for(it = dict_first(section); it; it = iter_next(it))
6966 chanserv_channel_read(iter_key(it), iter_data(it));
6968 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6969 for(it = dict_first(section); it; it = iter_next(it))
6970 chanserv_dnr_read(iter_key(it), iter_data(it));
6976 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6978 int high_present = 0;
6979 saxdb_start_record(ctx, KEY_USERS, 1);
6980 for(; uData; uData = uData->next)
6982 if((uData->access >= UL_PRESENT) && uData->present)
6984 saxdb_start_record(ctx, uData->handle->handle, 0);
6985 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6986 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6988 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6990 saxdb_write_string(ctx, KEY_INFO, uData->info);
6991 saxdb_end_record(ctx);
6993 saxdb_end_record(ctx);
6994 return high_present;
6998 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7002 saxdb_start_record(ctx, KEY_BANS, 1);
7003 for(; bData; bData = bData->next)
7005 saxdb_start_record(ctx, bData->mask, 0);
7006 saxdb_write_int(ctx, KEY_SET, bData->set);
7007 if(bData->triggered)
7008 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7010 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7012 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7014 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7015 saxdb_end_record(ctx);
7017 saxdb_end_record(ctx);
7021 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7023 saxdb_start_record(ctx, name, 0);
7024 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7025 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7027 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7029 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7031 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7033 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7034 saxdb_end_record(ctx);
7038 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7042 enum levelOption lvlOpt;
7043 enum charOption chOpt;
7045 saxdb_start_record(ctx, channel->channel->name, 1);
7047 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7048 saxdb_write_int(ctx, KEY_MAX, channel->max);
7050 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7051 if(channel->registrar)
7052 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7053 if(channel->greeting)
7054 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7055 if(channel->user_greeting)
7056 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7057 if(channel->topic_mask)
7058 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7059 if(channel->suspended)
7060 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7062 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7063 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7064 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7065 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7066 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7068 buf[0] = channel->chOpts[chOpt];
7070 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7072 saxdb_end_record(ctx);
7074 if(channel->modes.modes_set || channel->modes.modes_clear)
7076 mod_chanmode_format(&channel->modes, buf);
7077 saxdb_write_string(ctx, KEY_MODES, buf);
7080 high_present = chanserv_write_users(ctx, channel->users);
7081 chanserv_write_bans(ctx, channel->bans);
7083 if(dict_size(channel->notes))
7087 saxdb_start_record(ctx, KEY_NOTES, 1);
7088 for(it = dict_first(channel->notes); it; it = iter_next(it))
7090 struct note *note = iter_data(it);
7091 saxdb_start_record(ctx, iter_key(it), 0);
7092 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7093 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7094 saxdb_end_record(ctx);
7096 saxdb_end_record(ctx);
7099 if(channel->ownerTransfer)
7100 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7101 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7102 saxdb_end_record(ctx);
7106 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7110 saxdb_start_record(ctx, ntype->name, 0);
7111 switch(ntype->set_access_type)
7113 case NOTE_SET_CHANNEL_ACCESS:
7114 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7116 case NOTE_SET_CHANNEL_SETTER:
7117 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7119 case NOTE_SET_PRIVILEGED: default:
7120 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7123 switch(ntype->visible_type)
7125 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7126 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7127 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7129 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7130 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7131 saxdb_end_record(ctx);
7135 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7137 struct do_not_register *dnr;
7140 for(it = dict_first(dnrs); it; it = iter_next(it))
7142 dnr = iter_data(it);
7143 saxdb_start_record(ctx, dnr->chan_name, 0);
7145 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7146 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7147 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7148 saxdb_end_record(ctx);
7153 chanserv_saxdb_write(struct saxdb_context *ctx)
7156 struct chanData *channel;
7159 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7160 for(it = dict_first(note_types); it; it = iter_next(it))
7161 chanserv_write_note_type(ctx, iter_data(it));
7162 saxdb_end_record(ctx);
7165 saxdb_start_record(ctx, KEY_DNR, 1);
7166 write_dnrs_helper(ctx, handle_dnrs);
7167 write_dnrs_helper(ctx, plain_dnrs);
7168 write_dnrs_helper(ctx, mask_dnrs);
7169 saxdb_end_record(ctx);
7172 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7173 for(channel = channelList; channel; channel = channel->next)
7174 chanserv_write_channel(ctx, channel);
7175 saxdb_end_record(ctx);
7181 chanserv_db_cleanup(void) {
7183 unreg_part_func(handle_part);
7185 unregister_channel(channelList, "terminating.");
7186 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7187 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7188 free(chanserv_conf.support_channels.list);
7189 dict_delete(handle_dnrs);
7190 dict_delete(plain_dnrs);
7191 dict_delete(mask_dnrs);
7192 dict_delete(note_types);
7193 free_string_list(chanserv_conf.eightball);
7194 free_string_list(chanserv_conf.old_ban_names);
7195 free_string_list(chanserv_conf.set_shows);
7196 free(set_shows_list.list);
7197 free(uset_shows_list.list);
7200 struct userData *helper = helperList;
7201 helperList = helperList->next;
7206 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7207 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7208 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7211 init_chanserv(const char *nick)
7213 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7214 conf_register_reload(chanserv_conf_read);
7218 reg_server_link_func(handle_server_link);
7219 reg_new_channel_func(handle_new_channel);
7220 reg_join_func(handle_join);
7221 reg_part_func(handle_part);
7222 reg_kick_func(handle_kick);
7223 reg_topic_func(handle_topic);
7224 reg_mode_change_func(handle_mode);
7225 reg_nick_change_func(handle_nick_change);
7226 reg_auth_func(handle_auth);
7229 reg_handle_rename_func(handle_rename);
7230 reg_unreg_func(handle_unreg);
7232 handle_dnrs = dict_new();
7233 dict_set_free_data(handle_dnrs, free);
7234 plain_dnrs = dict_new();
7235 dict_set_free_data(plain_dnrs, free);
7236 mask_dnrs = dict_new();
7237 dict_set_free_data(mask_dnrs, free);
7239 reg_svccmd_unbind_func(handle_svccmd_unbind);
7240 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7241 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7242 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7243 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7244 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7245 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7246 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7247 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7248 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7250 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7251 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7253 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7254 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7255 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7256 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7257 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7259 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7260 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7261 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7262 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7263 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7265 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7266 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7267 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7268 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7270 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7271 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7272 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7273 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7274 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7275 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7276 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7277 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7279 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7280 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7281 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7282 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7283 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7284 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7285 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7286 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7287 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7288 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7289 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7290 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7291 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7292 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7294 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7295 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7296 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7297 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7298 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7300 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7301 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7303 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7304 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7305 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7306 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7307 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7308 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7309 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7310 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7311 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7312 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7313 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7315 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7316 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7318 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7319 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7320 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7321 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7323 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7324 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7325 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7326 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7327 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7329 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7330 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7331 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7332 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7333 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7334 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7336 /* Channel options */
7337 DEFINE_CHANNEL_OPTION(defaulttopic);
7338 DEFINE_CHANNEL_OPTION(topicmask);
7339 DEFINE_CHANNEL_OPTION(greeting);
7340 DEFINE_CHANNEL_OPTION(usergreeting);
7341 DEFINE_CHANNEL_OPTION(modes);
7342 DEFINE_CHANNEL_OPTION(enfops);
7343 DEFINE_CHANNEL_OPTION(giveops);
7344 DEFINE_CHANNEL_OPTION(protect);
7345 DEFINE_CHANNEL_OPTION(enfmodes);
7346 DEFINE_CHANNEL_OPTION(enftopic);
7347 DEFINE_CHANNEL_OPTION(pubcmd);
7348 DEFINE_CHANNEL_OPTION(givevoice);
7349 DEFINE_CHANNEL_OPTION(userinfo);
7350 DEFINE_CHANNEL_OPTION(dynlimit);
7351 DEFINE_CHANNEL_OPTION(topicsnarf);
7352 DEFINE_CHANNEL_OPTION(nodelete);
7353 DEFINE_CHANNEL_OPTION(toys);
7354 DEFINE_CHANNEL_OPTION(setters);
7355 DEFINE_CHANNEL_OPTION(topicrefresh);
7356 DEFINE_CHANNEL_OPTION(ctcpusers);
7357 DEFINE_CHANNEL_OPTION(ctcpreaction);
7358 DEFINE_CHANNEL_OPTION(inviteme);
7360 DEFINE_CHANNEL_OPTION(offchannel);
7361 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7363 /* Alias set topic to set defaulttopic for compatibility. */
7364 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7367 DEFINE_USER_OPTION(noautoop);
7368 DEFINE_USER_OPTION(autoinvite);
7369 DEFINE_USER_OPTION(info);
7371 /* Alias uset autovoice to uset autoop. */
7372 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7374 note_types = dict_new();
7375 dict_set_free_data(note_types, chanserv_deref_note_type);
7378 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7379 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7380 service_register(chanserv)->trigger = '!';
7381 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7383 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7385 if(chanserv_conf.channel_expire_frequency)
7386 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7388 if(chanserv_conf.refresh_period)
7390 time_t next_refresh;
7391 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7392 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7395 reg_exit_func(chanserv_db_cleanup);
7396 message_register_table(msgtab);