1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab
[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
176 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
177 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
178 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
179 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
181 /* Removing yourself from a channel. */
182 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
183 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
184 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
186 /* User management */
187 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
188 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
189 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
190 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
191 { "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." },
192 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
193 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
194 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
196 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
197 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
198 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
199 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
200 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
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 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 GiveHalfOps (%d)." },
246 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
247 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
248 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
249 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
250 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
251 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
252 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
253 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
254 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
255 { "CSMSG_SET_MODES", "$bModes $b %s" },
256 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
257 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
258 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
259 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
260 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
261 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
262 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
263 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
264 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
265 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
266 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
267 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
268 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
269 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
270 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
271 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
272 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
273 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
274 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
275 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
276 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
277 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
278 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
279 { "CSMSG_USET_INFO", "$bInfo $b %s" },
281 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
282 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
283 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
284 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
285 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
286 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
287 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
288 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
289 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
290 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
291 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
292 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
293 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
294 { "CSMSG_PROTECT_NONE", "No users will be protected." },
295 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
296 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
297 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
298 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
299 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
300 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
301 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
302 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
303 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
304 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
305 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
308 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
309 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
310 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
311 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
312 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
313 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
314 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
315 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
317 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
318 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
319 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
321 /* Channel userlist */
322 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
323 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
324 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
325 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
327 /* Channel note list */
328 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
329 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
330 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
331 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
332 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
333 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
334 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
335 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
336 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
337 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
338 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
339 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
340 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
341 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
342 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
344 /* Channel [un]suspension */
345 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
346 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
347 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
348 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
349 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
350 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
351 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
353 /* Access information */
354 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
355 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
356 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
357 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
358 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
359 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
360 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
361 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
362 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
363 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
364 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
366 /* Seen information */
367 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
368 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
369 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
370 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
372 /* Names information */
373 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
374 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
376 /* Channel information */
377 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
378 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
379 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
380 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
381 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
382 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
383 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
384 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
385 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
386 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
387 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
388 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
390 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
396 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
397 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
399 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
400 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
401 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
402 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
403 { "CSMSG_PEEK_OPS", "$bOps:$b" },
404 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
406 /* Network information */
407 { "CSMSG_NETWORK_INFO", "Network Information:" },
408 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
409 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
410 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
411 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
412 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
413 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
414 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
415 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
418 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
419 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
420 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
422 /* Channel searches */
423 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
424 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
425 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
426 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
428 /* Channel configuration */
429 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
430 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
431 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
434 { "CSMSG_USER_OPTIONS", "User Options:" },
435 { "CSMSG_USER_PROTECTED", "That user is protected." },
438 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
439 { "CSMSG_PING_RESPONSE", "Pong!" },
440 { "CSMSG_WUT_RESPONSE", "wut" },
441 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
442 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
443 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
444 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
445 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
446 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
447 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
450 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
454 /* eject_user and unban_user flags */
455 #define ACTION_KICK 0x0001
456 #define ACTION_BAN 0x0002
457 #define ACTION_ADD_BAN 0x0004
458 #define ACTION_ADD_TIMED_BAN 0x0008
459 #define ACTION_UNBAN 0x0010
460 #define ACTION_DEL_BAN 0x0020
462 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
463 #define MODELEN 40 + KEYLEN
467 #define CSFUNC_ARGS user, channel, argc, argv, cmd
469 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
470 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
471 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
472 reply("MSG_MISSING_PARAMS", argv[0]); \
476 DECLARE_LIST(dnrList
, struct do_not_register
*);
477 DEFINE_LIST(dnrList
, struct do_not_register
*);
479 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
481 struct userNode
*chanserv
;
484 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
485 static struct log_type
*CS_LOG
;
489 struct channelList support_channels
;
490 struct mod_chanmode default_modes
;
492 unsigned long db_backup_frequency
;
493 unsigned long channel_expire_frequency
;
496 unsigned int adjust_delay
;
497 long channel_expire_delay
;
498 unsigned int nodelete_level
;
500 unsigned int adjust_threshold
;
501 int join_flood_threshold
;
503 unsigned int greeting_length
;
504 unsigned int refresh_period
;
505 unsigned int giveownership_period
;
507 unsigned int max_owned
;
508 unsigned int max_chan_users
;
509 unsigned int max_chan_bans
;
510 unsigned int max_userinfo_length
;
512 unsigned int use_registered_mode
;
514 struct string_list
*set_shows
;
515 struct string_list
*eightball
;
516 struct string_list
*old_ban_names
;
518 const char *ctcp_short_ban_duration
;
519 const char *ctcp_long_ban_duration
;
521 const char *irc_operator_epithet
;
522 const char *network_helper_epithet
;
523 const char *support_helper_epithet
;
528 struct userNode
*user
;
529 struct userNode
*bot
;
530 struct chanNode
*channel
;
532 unsigned short lowest
;
533 unsigned short highest
;
534 struct userData
**users
;
535 struct helpfile_table table
;
538 enum note_access_type
540 NOTE_SET_CHANNEL_ACCESS
,
541 NOTE_SET_CHANNEL_SETTER
,
545 enum note_visible_type
548 NOTE_VIS_CHANNEL_USERS
,
554 enum note_access_type set_access_type
;
556 unsigned int min_opserv
;
557 unsigned short min_ulevel
;
559 enum note_visible_type visible_type
;
560 unsigned int max_length
;
567 struct note_type
*type
;
568 char setter
[NICKSERV_HANDLE_LEN
+1];
572 static unsigned int registered_channels
;
573 static unsigned int banCount
;
575 static const struct {
578 unsigned short level
;
581 { "peon", "Peon", UL_PEON
, '+' },
582 { "halfop", "HalfOp", UL_HALFOP
, '%' },
583 { "op", "Op", UL_OP
, '@' },
584 { "manager", "Manager", UL_MANAGER
, '%' },
585 { "coowner", "Coowner", UL_COOWNER
, '*' },
586 { "owner", "Owner", UL_OWNER
, '!' },
587 { "helper", "BUG:", UL_HELPER
, 'X' }
590 static const struct {
593 unsigned short default_value
;
594 unsigned int old_idx
;
595 unsigned int old_flag
;
596 unsigned short flag_value
;
598 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
599 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
600 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
601 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
602 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
603 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
604 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
605 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
606 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
607 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
608 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
609 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
610 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
613 struct charOptionValues
{
616 } protectValues
[] = {
617 { 'a', "CSMSG_PROTECT_ALL" },
618 { 'e', "CSMSG_PROTECT_EQUAL" },
619 { 'l', "CSMSG_PROTECT_LOWER" },
620 { 'n', "CSMSG_PROTECT_NONE" }
622 { 'd', "CSMSG_TOYS_DISABLED" },
623 { 'n', "CSMSG_TOYS_PRIVATE" },
624 { 'p', "CSMSG_TOYS_PUBLIC" }
625 }, topicRefreshValues
[] = {
626 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
627 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
628 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
629 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
630 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
631 }, ctcpReactionValues
[] = {
632 { 'k', "CSMSG_CTCPREACTION_KICK" },
633 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
634 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
635 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
638 static const struct {
642 unsigned int old_idx
;
644 struct charOptionValues
*values
;
646 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
647 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
648 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
649 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
652 struct userData
*helperList
;
653 struct chanData
*channelList
;
654 static struct module *chanserv_module
;
655 static unsigned int userCount
;
657 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
658 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
659 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
662 user_level_from_name(const char *name
, unsigned short clamp_level
)
664 unsigned int level
= 0, ii
;
666 level
= strtoul(name
, NULL
, 10);
667 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
668 if(!irccasecmp(name
, accessLevels
[ii
].name
))
669 level
= accessLevels
[ii
].level
;
670 if(level
> clamp_level
)
676 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
679 *minl
= strtoul(arg
, &sep
, 10);
687 *maxl
= strtoul(sep
+1, &sep
, 10);
695 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
697 struct userData
*uData
, **head
;
699 if(!channel
|| !handle
)
702 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
703 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
705 for(uData
= helperList
;
706 uData
&& uData
->handle
!= handle
;
707 uData
= uData
->next
);
711 uData
= calloc(1, sizeof(struct userData
));
712 uData
->handle
= handle
;
714 uData
->access
= UL_HELPER
;
720 uData
->next
= helperList
;
722 helperList
->prev
= uData
;
730 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
731 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
734 head
= &(channel
->users
);
737 if(uData
&& (uData
!= *head
))
739 /* Shuffle the user to the head of whatever list he was in. */
741 uData
->next
->prev
= uData
->prev
;
743 uData
->prev
->next
= uData
->next
;
749 (**head
).prev
= uData
;
756 /* Returns non-zero if user has at least the minimum access.
757 * exempt_owner is set when handling !set, so the owner can set things
760 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
762 struct userData
*uData
;
763 struct chanData
*cData
= channel
->channel_info
;
764 unsigned short minimum
= cData
->lvlOpts
[opt
];
767 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
770 if(minimum
<= uData
->access
)
772 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
777 /* Scan for other users authenticated to the same handle
778 still in the channel. If so, keep them listed as present.
780 user is optional, if not null, it skips checking that userNode
781 (for the handle_part function) */
783 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
787 if(IsSuspended(uData
->channel
)
788 || IsUserSuspended(uData
)
789 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
801 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
803 unsigned int eflags
, argc
;
805 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
807 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
808 if(!channel
->channel_info
809 || IsSuspended(channel
->channel_info
)
811 || !ircncasecmp(text
, "ACTION ", 7))
813 /* Figure out the minimum level needed to CTCP the channel */
814 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
816 /* We need to enforce against them; do so. */
819 argv
[1] = user
->nick
;
821 if(GetUserMode(channel
, user
))
822 eflags
|= ACTION_KICK
;
823 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
824 default: case 'k': /* just do the kick */ break;
826 eflags
|= ACTION_BAN
;
829 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
830 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
833 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
834 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
837 argv
[argc
++] = bad_ctcp_reason
;
838 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
842 chanserv_create_note_type(const char *name
)
844 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
845 strcpy(ntype
->name
, name
);
847 dict_insert(note_types
, ntype
->name
, ntype
);
852 chanserv_deref_note_type(void *data
)
854 struct note_type
*ntype
= data
;
856 if(--ntype
->refs
> 0)
862 chanserv_flush_note_type(struct note_type
*ntype
)
864 struct chanData
*cData
;
865 for(cData
= channelList
; cData
; cData
= cData
->next
)
866 dict_remove(cData
->notes
, ntype
->name
);
870 chanserv_truncate_notes(struct note_type
*ntype
)
872 struct chanData
*cData
;
874 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
876 for(cData
= channelList
; cData
; cData
= cData
->next
) {
877 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
880 if(strlen(note
->note
) <= ntype
->max_length
)
882 dict_remove2(cData
->notes
, ntype
->name
, 1);
883 note
= realloc(note
, size
);
884 note
->note
[ntype
->max_length
] = 0;
885 dict_insert(cData
->notes
, ntype
->name
, note
);
889 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
892 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
895 unsigned int len
= strlen(text
);
897 if(len
> type
->max_length
) len
= type
->max_length
;
898 note
= calloc(1, sizeof(*note
) + len
);
900 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
901 memcpy(note
->note
, text
, len
);
903 dict_insert(channel
->notes
, type
->name
, note
);
909 chanserv_free_note(void *data
)
911 struct note
*note
= data
;
913 chanserv_deref_note_type(note
->type
);
914 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
918 static MODCMD_FUNC(cmd_createnote
) {
919 struct note_type
*ntype
;
920 unsigned int arg
= 1, existed
= 0, max_length
;
922 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
925 ntype
= chanserv_create_note_type(argv
[arg
]);
926 if(!irccasecmp(argv
[++arg
], "privileged"))
929 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
930 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
932 else if(!irccasecmp(argv
[arg
], "channel"))
934 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
937 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
940 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
941 ntype
->set_access
.min_ulevel
= ulvl
;
943 else if(!irccasecmp(argv
[arg
], "setter"))
945 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
949 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
953 if(!irccasecmp(argv
[++arg
], "privileged"))
954 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
955 else if(!irccasecmp(argv
[arg
], "channel_users"))
956 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
957 else if(!irccasecmp(argv
[arg
], "all"))
958 ntype
->visible_type
= NOTE_VIS_ALL
;
960 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
964 if((arg
+1) >= argc
) {
965 reply("MSG_MISSING_PARAMS", argv
[0]);
968 max_length
= strtoul(argv
[++arg
], NULL
, 0);
969 if(max_length
< 20 || max_length
> 450)
971 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
974 if(existed
&& (max_length
< ntype
->max_length
))
976 ntype
->max_length
= max_length
;
977 chanserv_truncate_notes(ntype
);
979 ntype
->max_length
= max_length
;
982 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
984 reply("CSMSG_NOTE_CREATED", ntype
->name
);
989 dict_remove(note_types
, ntype
->name
);
993 static MODCMD_FUNC(cmd_removenote
) {
994 struct note_type
*ntype
;
997 ntype
= dict_find(note_types
, argv
[1], NULL
);
998 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1001 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1008 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1011 chanserv_flush_note_type(ntype
);
1013 dict_remove(note_types
, argv
[1]);
1014 reply("CSMSG_NOTE_DELETED", argv
[1]);
1019 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1023 if(orig
->modes_set
& change
->modes_clear
)
1025 if(orig
->modes_clear
& change
->modes_set
)
1027 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1028 && strcmp(orig
->new_key
, change
->new_key
))
1030 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1031 && (orig
->new_limit
!= change
->new_limit
))
1036 static char max_length_text
[MAXLEN
+1][16];
1038 static struct helpfile_expansion
1039 chanserv_expand_variable(const char *variable
)
1041 struct helpfile_expansion exp
;
1043 if(!irccasecmp(variable
, "notes"))
1046 exp
.type
= HF_TABLE
;
1047 exp
.value
.table
.length
= 1;
1048 exp
.value
.table
.width
= 3;
1049 exp
.value
.table
.flags
= 0;
1050 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1051 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1052 exp
.value
.table
.contents
[0][0] = "Note Type";
1053 exp
.value
.table
.contents
[0][1] = "Visibility";
1054 exp
.value
.table
.contents
[0][2] = "Max Length";
1055 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1057 struct note_type
*ntype
= iter_data(it
);
1060 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1061 row
= exp
.value
.table
.length
++;
1062 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1063 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1064 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1065 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1067 if(!max_length_text
[ntype
->max_length
][0])
1068 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1069 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1074 exp
.type
= HF_STRING
;
1075 exp
.value
.str
= NULL
;
1079 static struct chanData
*
1080 register_channel(struct chanNode
*cNode
, char *registrar
)
1082 struct chanData
*channel
;
1083 enum levelOption lvlOpt
;
1084 enum charOption chOpt
;
1086 channel
= calloc(1, sizeof(struct chanData
));
1088 channel
->notes
= dict_new();
1089 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1091 channel
->registrar
= strdup(registrar
);
1092 channel
->registered
= now
;
1093 channel
->visited
= now
;
1094 channel
->limitAdjusted
= now
;
1095 channel
->ownerTransfer
= now
;
1096 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1097 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1098 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1099 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1100 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1102 channel
->prev
= NULL
;
1103 channel
->next
= channelList
;
1106 channelList
->prev
= channel
;
1107 channelList
= channel
;
1108 registered_channels
++;
1110 channel
->channel
= cNode
;
1112 cNode
->channel_info
= channel
;
1117 static struct userData
*
1118 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1120 struct userData
*ud
;
1122 if(access
> UL_OWNER
)
1125 ud
= calloc(1, sizeof(*ud
));
1126 ud
->channel
= channel
;
1127 ud
->handle
= handle
;
1129 ud
->access
= access
;
1130 ud
->info
= info
? strdup(info
) : NULL
;
1133 ud
->next
= channel
->users
;
1135 channel
->users
->prev
= ud
;
1136 channel
->users
= ud
;
1138 channel
->userCount
++;
1142 ud
->u_next
= ud
->handle
->channels
;
1144 ud
->u_next
->u_prev
= ud
;
1145 ud
->handle
->channels
= ud
;
1150 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1153 del_channel_user(struct userData
*user
, int do_gc
)
1155 struct chanData
*channel
= user
->channel
;
1157 channel
->userCount
--;
1161 user
->prev
->next
= user
->next
;
1163 channel
->users
= user
->next
;
1165 user
->next
->prev
= user
->prev
;
1168 user
->u_prev
->u_next
= user
->u_next
;
1170 user
->handle
->channels
= user
->u_next
;
1172 user
->u_next
->u_prev
= user
->u_prev
;
1176 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1177 unregister_channel(channel
, "lost all users.");
1180 static void expire_ban(void *data
);
1182 static struct banData
*
1183 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1186 unsigned int ii
, l1
, l2
;
1191 bd
= malloc(sizeof(struct banData
));
1193 bd
->channel
= channel
;
1195 bd
->triggered
= triggered
;
1196 bd
->expires
= expires
;
1198 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1200 extern const char *hidden_host_suffix
;
1201 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1205 l2
= strlen(old_name
);
1208 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1210 new_mask
= alloca(MAXLEN
);
1211 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1214 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1216 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1217 bd
->reason
= reason
? strdup(reason
) : NULL
;
1220 timeq_add(expires
, expire_ban
, bd
);
1223 bd
->next
= channel
->bans
;
1225 channel
->bans
->prev
= bd
;
1227 channel
->banCount
++;
1234 del_channel_ban(struct banData
*ban
)
1236 ban
->channel
->banCount
--;
1240 ban
->prev
->next
= ban
->next
;
1242 ban
->channel
->bans
= ban
->next
;
1245 ban
->next
->prev
= ban
->prev
;
1248 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1257 expire_ban(void *data
)
1259 struct banData
*bd
= data
;
1260 if(!IsSuspended(bd
->channel
))
1262 struct banList bans
;
1263 struct mod_chanmode change
;
1265 bans
= bd
->channel
->channel
->banlist
;
1266 mod_chanmode_init(&change
);
1267 for(ii
=0; ii
<bans
.used
; ii
++)
1269 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1272 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1273 change
.args
[0].u
.hostmask
= bd
->mask
;
1274 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1280 del_channel_ban(bd
);
1283 static void chanserv_expire_suspension(void *data
);
1286 unregister_channel(struct chanData
*channel
, const char *reason
)
1288 struct mod_chanmode change
;
1289 char msgbuf
[MAXLEN
];
1291 /* After channel unregistration, the following must be cleaned
1293 - Channel information.
1296 - Channel suspension data.
1297 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1303 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1305 if(chanserv_conf
.use_registered_mode
)
1307 mod_chanmode_init(&change
);
1308 change
.modes_clear
|= MODE_REGISTERED
;
1309 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1312 while(channel
->users
)
1313 del_channel_user(channel
->users
, 0);
1315 while(channel
->bans
)
1316 del_channel_ban(channel
->bans
);
1318 free(channel
->topic
);
1319 free(channel
->registrar
);
1320 free(channel
->greeting
);
1321 free(channel
->user_greeting
);
1322 free(channel
->topic_mask
);
1325 channel
->prev
->next
= channel
->next
;
1327 channelList
= channel
->next
;
1330 channel
->next
->prev
= channel
->prev
;
1332 if(channel
->suspended
)
1334 struct chanNode
*cNode
= channel
->channel
;
1335 struct suspended
*suspended
, *next_suspended
;
1337 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1339 next_suspended
= suspended
->previous
;
1340 free(suspended
->suspender
);
1341 free(suspended
->reason
);
1342 if(suspended
->expires
)
1343 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1348 cNode
->channel_info
= NULL
;
1350 channel
->channel
->channel_info
= NULL
;
1352 dict_delete(channel
->notes
);
1353 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1354 if(!IsSuspended(channel
))
1355 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1356 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1357 UnlockChannel(channel
->channel
);
1359 registered_channels
--;
1363 expire_channels(UNUSED_ARG(void *data
))
1365 struct chanData
*channel
, *next
;
1366 struct userData
*user
;
1367 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1369 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1370 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1372 for(channel
= channelList
; channel
; channel
= next
)
1374 next
= channel
->next
;
1376 /* See if the channel can be expired. */
1377 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1378 || IsProtected(channel
))
1381 /* Make sure there are no high-ranking users still in the channel. */
1382 for(user
=channel
->users
; user
; user
=user
->next
)
1383 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1388 /* Unregister the channel */
1389 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1390 unregister_channel(channel
, "registration expired.");
1393 if(chanserv_conf
.channel_expire_frequency
)
1394 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1398 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1400 char protect
= channel
->chOpts
[chProtect
];
1401 struct userData
*cs_victim
, *cs_aggressor
;
1403 /* Don't protect if no one is to be protected, someone is attacking
1404 himself, or if the aggressor is an IRC Operator. */
1405 if(protect
== 'n' || victim
== aggressor
|| IsOper(aggressor
))
1408 /* Don't protect if the victim isn't authenticated (because they
1409 can't be a channel user), unless we are to protect non-users
1411 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1412 if(protect
!= 'a' && !cs_victim
)
1415 /* Protect if the aggressor isn't a user because at this point,
1416 the aggressor can only be less than or equal to the victim. */
1417 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1421 /* If the aggressor was a user, then the victim can't be helped. */
1428 if(cs_victim
->access
> cs_aggressor
->access
)
1433 if(cs_victim
->access
>= cs_aggressor
->access
)
1442 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1444 struct chanData
*cData
= channel
->channel_info
;
1445 struct userData
*cs_victim
;
1447 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1448 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveOps
]))
1449 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1451 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1459 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1461 struct chanData
*cData
= channel
->channel_info
;
1462 struct userData
*cs_victim
;
1464 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1465 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveHalfOps
]))
1466 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1468 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1477 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1479 if(IsService(victim
))
1481 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1485 if(protect_user(victim
, user
, channel
->channel_info
))
1487 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1495 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1497 if(IsService(victim
))
1499 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1503 if(protect_user(victim
, user
, channel
->channel_info
))
1505 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1512 static struct do_not_register
*
1513 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1515 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1516 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1517 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1518 strcpy(dnr
->reason
, reason
);
1520 if(dnr
->chan_name
[0] == '*')
1521 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1522 else if(strpbrk(dnr
->chan_name
, "*?"))
1523 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1525 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1529 static struct dnrList
1530 chanserv_find_dnrs(const char *chan_name
, struct handle_info
*handle
)
1532 struct dnrList list
;
1534 struct do_not_register
*dnr
;
1536 dnrList_init(&list
);
1537 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1538 dnrList_append(&list
, dnr
);
1539 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1540 dnrList_append(&list
, dnr
);
1542 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1543 if(match_ircglob(chan_name
, iter_key(it
)))
1544 dnrList_append(&list
, iter_data(it
));
1549 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, struct handle_info
*handle
)
1551 struct dnrList list
;
1552 struct do_not_register
*dnr
;
1554 char buf
[INTERVALLEN
];
1556 list
= chanserv_find_dnrs(chan_name
, handle
);
1557 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1559 dnr
= list
.list
[ii
];
1562 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1563 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1566 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1569 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1574 struct do_not_register
*
1575 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1577 struct do_not_register
*dnr
;
1580 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1584 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1586 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1587 if(match_ircglob(chan_name
, iter_key(it
)))
1588 return iter_data(it
);
1593 static CHANSERV_FUNC(cmd_noregister
)
1596 struct do_not_register
*dnr
;
1597 char buf
[INTERVALLEN
];
1598 unsigned int matches
;
1604 reply("CSMSG_DNR_SEARCH_RESULTS");
1606 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1608 dnr
= iter_data(it
);
1610 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1612 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1615 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1617 dnr
= iter_data(it
);
1619 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1621 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1624 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1626 dnr
= iter_data(it
);
1628 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1630 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1635 reply("MSG_MATCH_COUNT", matches
);
1637 reply("MSG_NO_MATCHES");
1643 if(!IsChannelName(target
) && (*target
!= '*'))
1645 reply("CSMSG_NOT_DNR", target
);
1651 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1652 if((*target
== '*') && !get_handle_info(target
+ 1))
1654 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1657 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1658 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1662 reply("CSMSG_DNR_SEARCH_RESULTS");
1664 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, get_handle_info(target
+ 1));
1666 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1668 reply("MSG_NO_MATCHES");
1672 static CHANSERV_FUNC(cmd_allowregister
)
1674 const char *chan_name
= argv
[1];
1676 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1678 dict_remove(handle_dnrs
, chan_name
+1);
1679 reply("CSMSG_DNR_REMOVED", chan_name
);
1681 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1683 dict_remove(plain_dnrs
, chan_name
);
1684 reply("CSMSG_DNR_REMOVED", chan_name
);
1686 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1688 dict_remove(mask_dnrs
, chan_name
);
1689 reply("CSMSG_DNR_REMOVED", chan_name
);
1693 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1700 chanserv_get_owned_count(struct handle_info
*hi
)
1702 struct userData
*cList
;
1705 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1706 if(cList
->access
== UL_OWNER
)
1711 static CHANSERV_FUNC(cmd_register
)
1713 struct mod_chanmode
*change
;
1714 struct handle_info
*handle
;
1715 struct chanData
*cData
;
1716 struct modeNode
*mn
;
1717 char reason
[MAXLEN
];
1719 unsigned int new_channel
, force
=0;
1720 struct do_not_register
*dnr
;
1724 if(channel
->channel_info
)
1726 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1730 if(channel
->bad_channel
)
1732 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1736 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1738 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1743 chan_name
= channel
->name
;
1747 if((argc
< 2) || !IsChannelName(argv
[1]))
1749 reply("MSG_NOT_CHANNEL_NAME");
1753 if(opserv_bad_channel(argv
[1]))
1755 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1760 chan_name
= argv
[1];
1763 if(argc
>= (new_channel
+2))
1765 if(!IsHelping(user
))
1767 reply("CSMSG_PROXY_FORBIDDEN");
1771 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1773 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1774 dnr
= chanserv_is_dnr(chan_name
, handle
);
1778 handle
= user
->handle_info
;
1779 dnr
= chanserv_is_dnr(chan_name
, handle
);
1783 if(!IsHelping(user
))
1784 reply("CSMSG_DNR_CHANNEL", chan_name
);
1786 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
);
1790 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1792 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1797 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1799 cData
= register_channel(channel
, user
->handle_info
->handle
);
1800 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1801 cData
->modes
= chanserv_conf
.default_modes
;
1802 if(chanserv_conf
.use_registered_mode
)
1803 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1804 change
= mod_chanmode_dup(&cData
->modes
, 1);
1805 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1806 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1808 mod_chanmode_announce(chanserv
, channel
, change
);
1809 mod_chanmode_free(change
);
1811 /* Initialize the channel's max user record. */
1812 cData
->max
= channel
->members
.used
;
1814 if(handle
!= user
->handle_info
)
1815 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1817 reply("CSMSG_REG_SUCCESS", channel
->name
);
1819 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1820 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1825 make_confirmation_string(struct userData
*uData
)
1827 static char strbuf
[16];
1832 for(src
= uData
->handle
->handle
; *src
; )
1833 accum
= accum
* 31 + toupper(*src
++);
1835 for(src
= uData
->channel
->channel
->name
; *src
; )
1836 accum
= accum
* 31 + toupper(*src
++);
1837 sprintf(strbuf
, "%08x", accum
);
1841 static CHANSERV_FUNC(cmd_unregister
)
1844 char reason
[MAXLEN
];
1845 struct chanData
*cData
;
1846 struct userData
*uData
;
1848 cData
= channel
->channel_info
;
1851 reply("CSMSG_NOT_REGISTERED", channel
->name
);
1855 uData
= GetChannelUser(cData
, user
->handle_info
);
1856 if(!uData
|| (uData
->access
< UL_OWNER
))
1858 reply("CSMSG_NO_ACCESS");
1862 if(IsProtected(cData
))
1864 reply("CSMSG_UNREG_NODELETE", channel
->name
);
1868 if(!IsHelping(user
))
1870 const char *confirm_string
;
1871 if(IsSuspended(cData
))
1873 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
1876 confirm_string
= make_confirmation_string(uData
);
1877 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
1879 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
1884 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
1885 name
= strdup(channel
->name
);
1886 unregister_channel(cData
, reason
);
1887 reply("CSMSG_UNREG_SUCCESS", name
);
1892 static CHANSERV_FUNC(cmd_move
)
1894 struct chanNode
*target
;
1895 struct modeNode
*mn
;
1896 struct userData
*uData
;
1897 char reason
[MAXLEN
];
1898 struct do_not_register
*dnr
;
1902 if(IsProtected(channel
->channel_info
))
1904 reply("CSMSG_MOVE_NODELETE", channel
->name
);
1908 if(!IsChannelName(argv
[1]))
1910 reply("MSG_NOT_CHANNEL_NAME");
1914 if(opserv_bad_channel(argv
[1]))
1916 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1920 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
1922 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
1924 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
1926 if(!IsHelping(user
))
1927 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
1929 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
);
1935 if(!(target
= GetChannel(argv
[1])))
1937 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1938 if(!IsSuspended(channel
->channel_info
))
1939 AddChannelUser(chanserv
, target
);
1941 else if(target
->channel_info
)
1943 reply("CSMSG_ALREADY_REGGED", target
->name
);
1946 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
1947 && !IsHelping(user
))
1949 reply("CSMSG_MUST_BE_OPPED", target
->name
);
1952 else if(!IsSuspended(channel
->channel_info
))
1954 struct mod_chanmode change
;
1955 mod_chanmode_init(&change
);
1957 change
.args
[0].mode
= MODE_CHANOP
;
1958 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
1959 mod_chanmode_announce(chanserv
, target
, &change
);
1962 /* Move the channel_info to the target channel; it
1963 shouldn't be necessary to clear timeq callbacks
1964 for the old channel. */
1965 target
->channel_info
= channel
->channel_info
;
1966 target
->channel_info
->channel
= target
;
1967 channel
->channel_info
= NULL
;
1969 reply("CSMSG_MOVE_SUCCESS", target
->name
);
1971 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
1972 if(!IsSuspended(target
->channel_info
))
1974 char reason2
[MAXLEN
];
1975 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
1976 DelChannelUser(chanserv
, channel
, reason2
, 0);
1978 UnlockChannel(channel
);
1979 LockChannel(target
);
1980 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1985 merge_users(struct chanData
*source
, struct chanData
*target
)
1987 struct userData
*suData
, *tuData
, *next
;
1993 /* Insert the source's users into the scratch area. */
1994 for(suData
= source
->users
; suData
; suData
= suData
->next
)
1995 dict_insert(merge
, suData
->handle
->handle
, suData
);
1997 /* Iterate through the target's users, looking for
1998 users common to both channels. The lower access is
1999 removed from either the scratch area or target user
2001 for(tuData
= target
->users
; tuData
; tuData
= next
)
2003 struct userData
*choice
;
2005 next
= tuData
->next
;
2007 /* If a source user exists with the same handle as a target
2008 channel's user, resolve the conflict by removing one. */
2009 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2013 /* Pick the data we want to keep. */
2014 /* If the access is the same, use the later seen time. */
2015 if(suData
->access
== tuData
->access
)
2016 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2017 else /* Otherwise, keep the higher access level. */
2018 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2020 /* Remove the user that wasn't picked. */
2021 if(choice
== tuData
)
2023 dict_remove(merge
, suData
->handle
->handle
);
2024 del_channel_user(suData
, 0);
2027 del_channel_user(tuData
, 0);
2030 /* Move the remaining users to the target channel. */
2031 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2033 suData
= iter_data(it
);
2035 /* Insert the user into the target channel's linked list. */
2036 suData
->prev
= NULL
;
2037 suData
->next
= target
->users
;
2038 suData
->channel
= target
;
2041 target
->users
->prev
= suData
;
2042 target
->users
= suData
;
2044 /* Update the user counts for the target channel; the
2045 source counts are left alone. */
2046 target
->userCount
++;
2049 /* Possible to assert (source->users == NULL) here. */
2050 source
->users
= NULL
;
2055 merge_bans(struct chanData
*source
, struct chanData
*target
)
2057 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2059 /* Hold on to the original head of the target ban list
2060 to avoid comparing source bans with source bans. */
2061 tFront
= target
->bans
;
2063 /* Perform a totally expensive O(n*m) merge, ick. */
2064 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2066 /* Flag to track whether the ban's been moved
2067 to the destination yet. */
2070 /* Possible to assert (sbData->prev == NULL) here. */
2071 sNext
= sbData
->next
;
2073 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2075 tNext
= tbData
->next
;
2077 /* Perform two comparisons between each source
2078 and target ban, conflicts are resolved by
2079 keeping the broader ban and copying the later
2080 expiration and triggered time. */
2081 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2083 /* There is a broader ban in the target channel that
2084 overrides one in the source channel; remove the
2085 source ban and break. */
2086 if(sbData
->expires
> tbData
->expires
)
2087 tbData
->expires
= sbData
->expires
;
2088 if(sbData
->triggered
> tbData
->triggered
)
2089 tbData
->triggered
= sbData
->triggered
;
2090 del_channel_ban(sbData
);
2093 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2095 /* There is a broader ban in the source channel that
2096 overrides one in the target channel; remove the
2097 target ban, fall through and move the source over. */
2098 if(tbData
->expires
> sbData
->expires
)
2099 sbData
->expires
= tbData
->expires
;
2100 if(tbData
->triggered
> sbData
->triggered
)
2101 sbData
->triggered
= tbData
->triggered
;
2102 if(tbData
== tFront
)
2104 del_channel_ban(tbData
);
2107 /* Source bans can override multiple target bans, so
2108 we allow a source to run through this loop multiple
2109 times, but we can only move it once. */
2114 /* Remove the source ban from the source ban list. */
2116 sbData
->next
->prev
= sbData
->prev
;
2118 /* Modify the source ban's associated channel. */
2119 sbData
->channel
= target
;
2121 /* Insert the ban into the target channel's linked list. */
2122 sbData
->prev
= NULL
;
2123 sbData
->next
= target
->bans
;
2126 target
->bans
->prev
= sbData
;
2127 target
->bans
= sbData
;
2129 /* Update the user counts for the target channel. */
2134 /* Possible to assert (source->bans == NULL) here. */
2135 source
->bans
= NULL
;
2139 merge_data(struct chanData
*source
, struct chanData
*target
)
2141 if(source
->visited
> target
->visited
)
2142 target
->visited
= source
->visited
;
2146 merge_channel(struct chanData
*source
, struct chanData
*target
)
2148 merge_users(source
, target
);
2149 merge_bans(source
, target
);
2150 merge_data(source
, target
);
2153 static CHANSERV_FUNC(cmd_merge
)
2155 struct userData
*target_user
;
2156 struct chanNode
*target
;
2157 char reason
[MAXLEN
];
2161 /* Make sure the target channel exists and is registered to the user
2162 performing the command. */
2163 if(!(target
= GetChannel(argv
[1])))
2165 reply("MSG_INVALID_CHANNEL");
2169 if(!target
->channel_info
)
2171 reply("CSMSG_NOT_REGISTERED", target
->name
);
2175 if(IsProtected(channel
->channel_info
))
2177 reply("CSMSG_MERGE_NODELETE");
2181 if(IsSuspended(target
->channel_info
))
2183 reply("CSMSG_MERGE_SUSPENDED");
2187 if(channel
== target
)
2189 reply("CSMSG_MERGE_SELF");
2193 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2194 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2196 reply("CSMSG_MERGE_NOT_OWNER");
2200 /* Merge the channel structures and associated data. */
2201 merge_channel(channel
->channel_info
, target
->channel_info
);
2202 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2203 unregister_channel(channel
->channel_info
, reason
);
2204 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2208 static CHANSERV_FUNC(cmd_opchan
)
2210 struct mod_chanmode change
;
2211 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2213 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2216 channel
->channel_info
->may_opchan
= 0;
2217 mod_chanmode_init(&change
);
2219 change
.args
[0].mode
= MODE_CHANOP
;
2220 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2221 mod_chanmode_announce(chanserv
, channel
, &change
);
2222 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2226 static CHANSERV_FUNC(cmd_adduser
)
2228 struct userData
*actee
;
2229 struct userData
*actor
;
2230 struct handle_info
*handle
;
2231 unsigned short access
;
2235 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2237 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2241 access
= user_level_from_name(argv
[2], UL_OWNER
);
2244 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2248 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2249 if(actor
->access
<= access
)
2251 reply("CSMSG_NO_BUMP_ACCESS");
2255 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2258 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2260 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, actee
->access
);
2264 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2265 scan_user_presence(actee
, NULL
);
2266 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, access
);
2270 static CHANSERV_FUNC(cmd_clvl
)
2272 struct handle_info
*handle
;
2273 struct userData
*victim
;
2274 struct userData
*actor
;
2275 unsigned short new_access
;
2276 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2280 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2282 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2285 if(handle
== user
->handle_info
&& !privileged
)
2287 reply("CSMSG_NO_SELF_CLVL");
2291 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2293 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2297 if(actor
->access
<= victim
->access
&& !privileged
)
2299 reply("MSG_USER_OUTRANKED", handle
->handle
);
2303 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2307 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2311 if(new_access
>= actor
->access
&& !privileged
)
2313 reply("CSMSG_NO_BUMP_ACCESS");
2317 victim
->access
= new_access
;
2318 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, new_access
, channel
->name
);
2322 static CHANSERV_FUNC(cmd_deluser
)
2324 struct handle_info
*handle
;
2325 struct userData
*victim
;
2326 struct userData
*actor
;
2327 unsigned short access
;
2332 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2334 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2337 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2339 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2345 access
= user_level_from_name(argv
[1], UL_OWNER
);
2348 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2351 if(access
!= victim
->access
)
2353 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, victim
->access
, argv
[1]);
2359 access
= victim
->access
;
2362 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2364 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2368 chan_name
= strdup(channel
->name
);
2369 del_channel_user(victim
, 1);
2370 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2376 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2378 struct userData
*actor
, *uData
, *next
;
2380 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2382 if(min_access
> max_access
)
2384 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2388 if((actor
->access
<= max_access
) && !IsHelping(user
))
2390 reply("CSMSG_NO_ACCESS");
2394 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2398 if((uData
->access
>= min_access
)
2399 && (uData
->access
<= max_access
)
2400 && match_ircglob(uData
->handle
->handle
, mask
))
2401 del_channel_user(uData
, 1);
2404 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2408 static CHANSERV_FUNC(cmd_mdelowner
)
2410 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2413 static CHANSERV_FUNC(cmd_mdelcoowner
)
2415 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2418 static CHANSERV_FUNC(cmd_mdelmanager
)
2420 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2423 static CHANSERV_FUNC(cmd_mdelop
)
2425 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2428 static CHANSERV_FUNC(cmd_mdelpeon
)
2430 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2433 static CHANSERV_FUNC(cmd_mdelhalfop
)
2435 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2440 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2442 struct banData
*bData
, *next
;
2443 char interval
[INTERVALLEN
];
2448 limit
= now
- duration
;
2449 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2453 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2456 del_channel_ban(bData
);
2460 intervalString(interval
, duration
, user
->handle_info
);
2461 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2466 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2468 struct userData
*actor
, *uData
, *next
;
2469 char interval
[INTERVALLEN
];
2473 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2474 if(min_access
> max_access
)
2476 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2480 if((actor
->access
<= max_access
) && !IsHelping(user
))
2482 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2487 limit
= now
- duration
;
2488 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2492 if((uData
->seen
> limit
) || uData
->present
)
2495 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2496 || (!max_access
&& (uData
->access
< actor
->access
)))
2498 del_channel_user(uData
, 1);
2506 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2508 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2512 static CHANSERV_FUNC(cmd_trim
)
2514 unsigned long duration
;
2515 unsigned short min_level
, max_level
;
2519 duration
= ParseInterval(argv
[2]);
2522 reply("CSMSG_CANNOT_TRIM");
2526 if(!irccasecmp(argv
[1], "bans"))
2528 cmd_trim_bans(user
, channel
, duration
);
2531 else if(!irccasecmp(argv
[1], "users"))
2533 cmd_trim_users(user
, channel
, 0, 0, duration
);
2536 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2538 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2541 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2543 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2548 reply("CSMSG_INVALID_TRIM", argv
[1]);
2553 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2554 to the user. cmd_all takes advantage of this. */
2555 static CHANSERV_FUNC(cmd_up
)
2557 struct mod_chanmode change
;
2558 struct userData
*uData
;
2561 mod_chanmode_init(&change
);
2563 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2564 if(!change
.args
[0].u
.member
)
2567 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2571 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2575 reply("CSMSG_GODMODE_UP", argv
[0]);
2578 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveOps
])
2580 change
.args
[0].mode
= MODE_CHANOP
;
2581 errmsg
= "CSMSG_ALREADY_OPPED";
2583 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
2585 change
.args
[0].mode
= MODE_HALFOP
;
2586 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2588 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveVoice
])
2590 change
.args
[0].mode
= MODE_VOICE
;
2591 errmsg
= "CSMSG_ALREADY_VOICED";
2596 reply("CSMSG_NO_ACCESS");
2599 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2600 if(!change
.args
[0].mode
)
2603 reply(errmsg
, channel
->name
);
2606 modcmd_chanmode_announce(&change
);
2610 static CHANSERV_FUNC(cmd_down
)
2612 struct mod_chanmode change
;
2614 mod_chanmode_init(&change
);
2616 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2617 if(!change
.args
[0].u
.member
)
2620 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2624 if(!change
.args
[0].u
.member
->modes
)
2627 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2631 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2632 modcmd_chanmode_announce(&change
);
2636 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
)
2638 struct userData
*cList
;
2640 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2642 if(IsSuspended(cList
->channel
)
2643 || IsUserSuspended(cList
)
2644 || !GetUserMode(cList
->channel
->channel
, user
))
2647 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2653 static CHANSERV_FUNC(cmd_upall
)
2655 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2658 static CHANSERV_FUNC(cmd_downall
)
2660 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2663 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2664 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2667 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
)
2669 unsigned int ii
, valid
;
2670 struct userNode
*victim
;
2671 struct mod_chanmode
*change
;
2673 change
= mod_chanmode_alloc(argc
- 1);
2675 for(ii
=valid
=0; ++ii
< argc
; )
2677 if(!(victim
= GetUserH(argv
[ii
])))
2679 change
->args
[valid
].mode
= mode
;
2680 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2681 if(!change
->args
[valid
].u
.member
)
2683 if(validate
&& !validate(user
, channel
, victim
))
2688 change
->argc
= valid
;
2689 if(valid
< (argc
-1))
2690 reply("CSMSG_PROCESS_FAILED");
2693 modcmd_chanmode_announce(change
);
2694 reply(action
, channel
->name
);
2696 mod_chanmode_free(change
);
2700 static CHANSERV_FUNC(cmd_op
)
2702 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2705 static CHANSERV_FUNC(cmd_hop
)
2707 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2710 static CHANSERV_FUNC(cmd_deop
)
2712 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2715 static CHANSERV_FUNC(cmd_dehop
)
2717 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2720 static CHANSERV_FUNC(cmd_voice
)
2722 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2725 static CHANSERV_FUNC(cmd_devoice
)
2727 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2731 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2737 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2739 struct modeNode
*mn
= channel
->members
.list
[ii
];
2741 if(IsService(mn
->user
))
2744 if(!user_matches_glob(mn
->user
, ban
, 1))
2747 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2751 victims
[(*victimCount
)++] = mn
;
2757 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2759 struct userNode
*victim
;
2760 struct modeNode
**victims
;
2761 unsigned int offset
, n
, victimCount
, duration
= 0;
2762 char *reason
= "Bye.", *ban
, *name
;
2763 char interval
[INTERVALLEN
];
2765 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2766 REQUIRE_PARAMS(offset
);
2769 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2770 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2772 /* Truncate the reason to a length of TOPICLEN, as
2773 the ircd does; however, leave room for an ellipsis
2774 and the kicker's nick. */
2775 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2779 if((victim
= GetUserH(argv
[1])))
2781 victims
= alloca(sizeof(victims
[0]));
2782 victims
[0] = GetUserMode(channel
, victim
);
2783 /* XXX: The comparison with ACTION_KICK is just because all
2784 * other actions can work on users outside the channel, and we
2785 * want to allow those (e.g. unbans) in that case. If we add
2786 * some other ejection action for in-channel users, change
2788 victimCount
= victims
[0] ? 1 : 0;
2790 if(IsService(victim
))
2792 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
2796 if((action
== ACTION_KICK
) && !victimCount
)
2798 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
2802 if(protect_user(victim
, user
, channel
->channel_info
))
2804 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2808 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
2809 name
= victim
->nick
;
2813 if(!is_ircmask(argv
[1]))
2815 reply("MSG_NICK_UNKNOWN", argv
[1]);
2819 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
2821 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
2823 reply("CSMSG_MASK_PROTECTED", argv
[1]);
2826 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
2828 victimCount
= 0; /* Dont deop etc ppl who match this */
2830 #ifdef entropy_lameness
2831 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
2833 reply("CSMSG_LAME_MASK", argv
[1]);
2838 if((action
== ACTION_KICK
) && (victimCount
== 0))
2840 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
2844 name
= ban
= strdup(argv
[1]);
2847 /* Truncate the ban in place if necessary; we must ensure
2848 that 'ban' is a valid ban mask before sanitizing it. */
2849 sanitize_ircmask(ban
);
2851 if(action
& ACTION_ADD_BAN
)
2853 struct banData
*bData
, *next
;
2855 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
2857 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
2862 if(action
& ACTION_ADD_TIMED_BAN
)
2864 duration
= ParseInterval(argv
[2]);
2868 reply("CSMSG_DURATION_TOO_LOW");
2872 else if(duration
> (86400 * 365 * 2))
2874 reply("CSMSG_DURATION_TOO_HIGH");
2880 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2882 if(match_ircglobs(bData
->mask
, ban
))
2884 int exact
= !irccasecmp(bData
->mask
, ban
);
2886 /* The ban is redundant; there is already a ban
2887 with the same effect in place. */
2891 free(bData
->reason
);
2892 bData
->reason
= strdup(reason
);
2893 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
2895 reply("CSMSG_REASON_CHANGE", ban
);
2899 if(exact
&& bData
->expires
)
2903 /* If the ban matches an existing one exactly,
2904 extend the expiration time if the provided
2905 duration is longer. */
2906 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
2908 bData
->expires
= now
+ duration
;
2919 /* Delete the expiration timeq entry and
2920 requeue if necessary. */
2921 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
2924 timeq_add(bData
->expires
, expire_ban
, bData
);
2928 /* automated kickban */
2931 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
2933 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2939 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2946 if(match_ircglobs(ban
, bData
->mask
))
2948 /* The ban we are adding makes previously existing
2949 bans redundant; silently remove them. */
2950 del_channel_ban(bData
);
2954 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
);
2956 name
= ban
= strdup(bData
->mask
);
2960 /* WHAT DOES THIS DO?? -Rubin */
2961 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
2963 extern const char *hidden_host_suffix
;
2964 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
2966 unsigned int l1
, l2
;
2969 l2
= strlen(old_name
);
2972 if(irccasecmp(ban
+ l1
- l2
, old_name
))
2974 new_mask
= malloc(MAXLEN
);
2975 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
2977 name
= ban
= new_mask
;
2982 if(action
& ACTION_BAN
)
2984 unsigned int exists
;
2985 struct mod_chanmode
*change
;
2987 if(channel
->banlist
.used
>= MAXBANS
)
2990 reply("CSMSG_BANLIST_FULL", channel
->name
);
2995 exists
= ChannelBanExists(channel
, ban
);
2996 change
= mod_chanmode_alloc(victimCount
+ 1);
2997 for(n
= 0; n
< victimCount
; ++n
)
2999 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3000 change
->args
[n
].u
.member
= victims
[n
];
3004 change
->args
[n
].mode
= MODE_BAN
;
3005 change
->args
[n
++].u
.hostmask
= ban
;
3009 modcmd_chanmode_announce(change
);
3011 mod_chanmode_announce(chanserv
, channel
, change
);
3012 mod_chanmode_free(change
);
3014 if(exists
&& (action
== ACTION_BAN
))
3017 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3023 if(action
& ACTION_KICK
)
3025 char kick_reason
[MAXLEN
];
3026 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3028 for(n
= 0; n
< victimCount
; n
++)
3029 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3034 /* No response, since it was automated. */
3036 else if(action
& ACTION_ADD_BAN
)
3039 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3041 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3043 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3044 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3045 else if(action
& ACTION_BAN
)
3046 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3047 else if(action
& ACTION_KICK
&& victimCount
)
3048 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3054 static CHANSERV_FUNC(cmd_kickban
)
3056 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3059 static CHANSERV_FUNC(cmd_kick
)
3061 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3064 static CHANSERV_FUNC(cmd_ban
)
3066 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3069 static CHANSERV_FUNC(cmd_addban
)
3071 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
3074 static CHANSERV_FUNC(cmd_addtimedban
)
3076 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
3079 static struct mod_chanmode
*
3080 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3082 struct mod_chanmode
*change
;
3083 unsigned char *match
;
3084 unsigned int ii
, count
;
3086 match
= alloca(bans
->used
);
3089 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3091 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3098 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3100 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3107 change
= mod_chanmode_alloc(count
);
3108 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3112 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3113 change
->args
[count
++].u
.hostmask
= bans
->list
[ii
]->ban
;
3119 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3121 struct userNode
*actee
;
3127 /* may want to allow a comma delimited list of users... */
3128 if(!(actee
= GetUserH(argv
[1])))
3130 if(!is_ircmask(argv
[1]))
3132 reply("MSG_NICK_UNKNOWN", argv
[1]);
3136 mask
= strdup(argv
[1]);
3139 /* We don't sanitize the mask here because ircu
3141 if(action
& ACTION_UNBAN
)
3143 struct mod_chanmode
*change
;
3144 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3147 modcmd_chanmode_announce(change
);
3148 mod_chanmode_free(change
);
3153 if(action
& ACTION_DEL_BAN
)
3155 struct banData
*ban
, *next
;
3157 ban
= channel
->channel_info
->bans
;
3161 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3164 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3169 del_channel_ban(ban
);
3176 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3178 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3184 static CHANSERV_FUNC(cmd_unban
)
3186 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3189 static CHANSERV_FUNC(cmd_delban
)
3191 /* it doesn't necessarily have to remove the channel ban - may want
3192 to make that an option. */
3193 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3196 static CHANSERV_FUNC(cmd_unbanme
)
3198 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3199 long flags
= ACTION_UNBAN
;
3201 /* remove permanent bans if the user has the proper access. */
3202 if(uData
->access
>= UL_MANAGER
)
3203 flags
|= ACTION_DEL_BAN
;
3205 argv
[1] = user
->nick
;
3206 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3209 static CHANSERV_FUNC(cmd_unbanall
)
3211 struct mod_chanmode
*change
;
3214 if(!channel
->banlist
.used
)
3216 reply("CSMSG_NO_BANS", channel
->name
);
3220 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3221 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3223 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3224 change
->args
[ii
].u
.hostmask
= channel
->banlist
.list
[ii
]->ban
;
3226 modcmd_chanmode_announce(change
);
3227 mod_chanmode_free(change
);
3228 reply("CSMSG_BANS_REMOVED", channel
->name
);
3232 static CHANSERV_FUNC(cmd_open
)
3234 struct mod_chanmode
*change
;
3236 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3238 change
= mod_chanmode_alloc(0);
3239 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3240 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3241 && channel
->channel_info
->modes
.modes_set
)
3242 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3243 modcmd_chanmode_announce(change
);
3244 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3245 mod_chanmode_free(change
);
3249 static CHANSERV_FUNC(cmd_myaccess
)
3251 static struct string_buffer sbuf
;
3252 struct handle_info
*target_handle
;
3253 struct userData
*uData
;
3256 target_handle
= user
->handle_info
;
3257 else if(!IsHelping(user
))
3259 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3262 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3265 if(!target_handle
->channels
)
3267 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3271 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3272 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3274 struct chanData
*cData
= uData
->channel
;
3276 if(uData
->access
> UL_OWNER
)
3278 if(IsProtected(cData
)
3279 && (target_handle
!= user
->handle_info
)
3280 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3283 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3284 if(uData
->flags
!= USER_AUTO_OP
)
3285 string_buffer_append(&sbuf
, ',');
3286 if(IsUserSuspended(uData
))
3287 string_buffer_append(&sbuf
, 's');
3288 if(IsUserAutoOp(uData
))
3290 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
3291 string_buffer_append(&sbuf
, 'o');
3292 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
3293 string_buffer_append(&sbuf
, 'h');
3294 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
3295 string_buffer_append(&sbuf
, 'v');
3297 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3298 string_buffer_append(&sbuf
, 'i');
3300 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3302 string_buffer_append_string(&sbuf
, ")]");
3303 string_buffer_append(&sbuf
, '\0');
3304 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3310 static CHANSERV_FUNC(cmd_access
)
3312 struct userNode
*target
;
3313 struct handle_info
*target_handle
;
3314 struct userData
*uData
;
3316 char prefix
[MAXLEN
];
3321 target_handle
= target
->handle_info
;
3323 else if((target
= GetUserH(argv
[1])))
3325 target_handle
= target
->handle_info
;
3327 else if(argv
[1][0] == '*')
3329 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3331 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3337 reply("MSG_NICK_UNKNOWN", argv
[1]);
3341 assert(target
|| target_handle
);
3343 if(target
== chanserv
)
3345 reply("CSMSG_IS_CHANSERV");
3353 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3358 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3361 reply("MSG_AUTHENTICATE");
3367 const char *epithet
= NULL
, *type
= NULL
;
3370 epithet
= chanserv_conf
.irc_operator_epithet
;
3373 else if(IsNetworkHelper(target
))
3375 epithet
= chanserv_conf
.network_helper_epithet
;
3376 type
= "network helper";
3378 else if(IsSupportHelper(target
))
3380 epithet
= chanserv_conf
.support_helper_epithet
;
3381 type
= "support helper";
3385 if(target_handle
->epithet
)
3386 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3388 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3390 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3394 sprintf(prefix
, "%s", target_handle
->handle
);
3397 if(!channel
->channel_info
)
3399 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3403 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3404 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3405 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3407 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, uData
->access
, channel
->name
);
3408 /* To prevent possible information leaks, only show infolines
3409 * if the requestor is in the channel or it's their own
3411 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3413 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3415 /* Likewise, only say it's suspended if the user has active
3416 * access in that channel or it's their own entry. */
3417 if(IsUserSuspended(uData
)
3418 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3419 || (user
->handle_info
== uData
->handle
)))
3421 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3426 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3433 zoot_list(struct listData
*list
)
3435 struct userData
*uData
;
3436 unsigned int start
, curr
, highest
, lowest
;
3437 struct helpfile_table tmp_table
;
3438 const char **temp
, *msg
;
3440 if(list
->table
.length
== 1)
3443 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3445 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3446 msg
= user_find_message(list
->user
, "MSG_NONE");
3447 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3449 tmp_table
.width
= list
->table
.width
;
3450 tmp_table
.flags
= list
->table
.flags
;
3451 list
->table
.contents
[0][0] = " ";
3452 highest
= list
->highest
;
3453 if(list
->lowest
!= 0)
3454 lowest
= list
->lowest
;
3455 else if(highest
< 100)
3458 lowest
= highest
- 100;
3459 for(start
= curr
= 1; curr
< list
->table
.length
; )
3461 uData
= list
->users
[curr
-1];
3462 list
->table
.contents
[curr
++][0] = " ";
3463 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3466 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, lowest
, highest
, list
->search
);
3468 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, lowest
, highest
);
3469 temp
= list
->table
.contents
[--start
];
3470 list
->table
.contents
[start
] = list
->table
.contents
[0];
3471 tmp_table
.contents
= list
->table
.contents
+ start
;
3472 tmp_table
.length
= curr
- start
;
3473 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3474 list
->table
.contents
[start
] = temp
;
3476 highest
= lowest
- 1;
3477 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3483 def_list(struct listData
*list
)
3487 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3489 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3490 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3491 if(list
->table
.length
== 1)
3493 msg
= user_find_message(list
->user
, "MSG_NONE");
3494 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3499 userData_access_comp(const void *arg_a
, const void *arg_b
)
3501 const struct userData
*a
= *(struct userData
**)arg_a
;
3502 const struct userData
*b
= *(struct userData
**)arg_b
;
3504 if(a
->access
!= b
->access
)
3505 res
= b
->access
- a
->access
;
3507 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3512 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3514 void (*send_list
)(struct listData
*);
3515 struct userData
*uData
;
3516 struct listData lData
;
3517 unsigned int matches
;
3521 lData
.bot
= cmd
->parent
->bot
;
3522 lData
.channel
= channel
;
3523 lData
.lowest
= lowest
;
3524 lData
.highest
= highest
;
3525 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3526 send_list
= def_list
;
3527 (void)zoot_list
; /* since it doesn't show user levels */
3529 if(user
->handle_info
)
3531 switch(user
->handle_info
->userlist_style
)
3533 case HI_STYLE_DEF
: send_list
= def_list
; break;
3534 case HI_STYLE_ZOOT
: send_list
= def_list
; break;
3538 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3540 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3542 if((uData
->access
< lowest
)
3543 || (uData
->access
> highest
)
3544 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3546 lData
.users
[matches
++] = uData
;
3548 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3550 lData
.table
.length
= matches
+1;
3551 lData
.table
.width
= 4;
3552 lData
.table
.flags
= TABLE_NO_FREE
;
3553 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3554 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3555 lData
.table
.contents
[0] = ary
;
3558 ary
[2] = "Last Seen";
3560 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3562 struct userData
*uData
= lData
.users
[matches
-1];
3563 char seen
[INTERVALLEN
];
3565 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3566 lData
.table
.contents
[matches
] = ary
;
3567 ary
[0] = strtab(uData
->access
);
3568 ary
[1] = uData
->handle
->handle
;
3571 else if(!uData
->seen
)
3574 ary
[2] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3575 ary
[2] = strdup(ary
[2]);
3576 if(IsUserSuspended(uData
))
3577 ary
[3] = "Suspended";
3578 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3579 ary
[3] = "Vacation";
3584 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3586 free((char*)lData
.table
.contents
[matches
][2]);
3587 free(lData
.table
.contents
[matches
]);
3589 free(lData
.table
.contents
[0]);
3590 free(lData
.table
.contents
);
3594 static CHANSERV_FUNC(cmd_users
)
3596 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3599 static CHANSERV_FUNC(cmd_wlist
)
3601 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3604 static CHANSERV_FUNC(cmd_clist
)
3606 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3609 static CHANSERV_FUNC(cmd_mlist
)
3611 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3614 static CHANSERV_FUNC(cmd_olist
)
3616 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3619 static CHANSERV_FUNC(cmd_hlist
)
3621 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3624 static CHANSERV_FUNC(cmd_plist
)
3626 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3629 static CHANSERV_FUNC(cmd_bans
)
3631 struct helpfile_table tbl
;
3632 unsigned int matches
= 0, timed
= 0, ii
;
3633 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3634 const char *msg_never
, *triggered
, *expires
;
3635 struct banData
*ban
, **bans
;
3642 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3644 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3646 if(search
&& !match_ircglobs(search
, ban
->mask
))
3648 bans
[matches
++] = ban
;
3653 tbl
.length
= matches
+ 1;
3654 tbl
.width
= 4 + timed
;
3656 tbl
.flags
= TABLE_NO_FREE
;
3657 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3658 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3659 tbl
.contents
[0][0] = "Mask";
3660 tbl
.contents
[0][1] = "Set By";
3661 tbl
.contents
[0][2] = "Triggered";
3664 tbl
.contents
[0][3] = "Expires";
3665 tbl
.contents
[0][4] = "Reason";
3668 tbl
.contents
[0][3] = "Reason";
3671 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3673 free(tbl
.contents
[0]);
3678 msg_never
= user_find_message(user
, "MSG_NEVER");
3679 for(ii
= 0; ii
< matches
; )
3685 else if(ban
->expires
)
3686 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3688 expires
= msg_never
;
3691 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3693 triggered
= msg_never
;
3695 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3696 tbl
.contents
[ii
][0] = ban
->mask
;
3697 tbl
.contents
[ii
][1] = ban
->owner
;
3698 tbl
.contents
[ii
][2] = strdup(triggered
);
3701 tbl
.contents
[ii
][3] = strdup(expires
);
3702 tbl
.contents
[ii
][4] = ban
->reason
;
3705 tbl
.contents
[ii
][3] = ban
->reason
;
3707 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3708 reply("MSG_MATCH_COUNT", matches
);
3709 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3711 free((char*)tbl
.contents
[ii
][2]);
3713 free((char*)tbl
.contents
[ii
][3]);
3714 free(tbl
.contents
[ii
]);
3716 free(tbl
.contents
[0]);
3722 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3724 struct chanData
*cData
= channel
->channel_info
;
3725 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3727 if(cData
->topic_mask
)
3728 return !match_ircglob(new_topic
, cData
->topic_mask
);
3729 else if(cData
->topic
)
3730 return irccasecmp(new_topic
, cData
->topic
);
3735 static CHANSERV_FUNC(cmd_topic
)
3737 struct chanData
*cData
;
3740 cData
= channel
->channel_info
;
3745 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
3746 reply("CSMSG_TOPIC_SET", cData
->topic
);
3750 reply("CSMSG_NO_TOPIC", channel
->name
);
3754 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
3755 /* If they say "!topic *", use an empty topic. */
3756 if((topic
[0] == '*') && (topic
[1] == 0))
3758 if(bad_topic(channel
, user
, topic
))
3760 char *topic_mask
= cData
->topic_mask
;
3763 char new_topic
[TOPICLEN
+1], tchar
;
3764 int pos
=0, starpos
=-1, dpos
=0, len
;
3766 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3773 len
= strlen(topic
);
3774 if((dpos
+ len
) > TOPICLEN
)
3775 len
= TOPICLEN
+ 1 - dpos
;
3776 memcpy(new_topic
+dpos
, topic
, len
);
3780 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
3781 default: new_topic
[dpos
++] = tchar
; break;
3784 if((dpos
> TOPICLEN
) || tchar
)
3787 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
3788 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
3791 new_topic
[dpos
] = 0;
3792 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
3794 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
3799 SetChannelTopic(channel
, chanserv
, topic
, 1);
3801 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
3803 /* Grab the topic and save it as the default topic. */
3805 cData
->topic
= strdup(channel
->topic
);
3811 static CHANSERV_FUNC(cmd_mode
)
3813 struct mod_chanmode
*change
;
3817 change
= &channel
->channel_info
->modes
;
3818 if(change
->modes_set
|| change
->modes_clear
) {
3819 modcmd_chanmode_announce(change
);
3820 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
3822 reply("CSMSG_NO_MODES", channel
->name
);
3826 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
3829 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
3833 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3834 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
3837 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
3838 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
3842 modcmd_chanmode_announce(change
);
3843 mod_chanmode_free(change
);
3844 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
3848 static CHANSERV_FUNC(cmd_invite
)
3850 struct userData
*uData
;
3851 struct userNode
*invite
;
3853 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3857 if(!(invite
= GetUserH(argv
[1])))
3859 reply("MSG_NICK_UNKNOWN", argv
[1]);
3866 if(GetUserMode(channel
, invite
))
3868 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
3876 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
3877 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
3880 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
3882 irc_invite(chanserv
, invite
, channel
);
3884 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
3889 static CHANSERV_FUNC(cmd_inviteme
)
3891 if(GetUserMode(channel
, user
))
3893 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
3896 if(channel
->channel_info
3897 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
3899 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
3902 irc_invite(cmd
->parent
->bot
, user
, channel
);
3907 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
3910 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
3912 /* We display things based on two dimensions:
3913 * - Issue time: present or absent
3914 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3915 * (in order of precedence, so something both expired and revoked
3916 * only counts as revoked)
3918 combo
= (suspended
->issued
? 4 : 0)
3919 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
3921 case 0: /* no issue time, indefinite expiration */
3922 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
3924 case 1: /* no issue time, expires in future */
3925 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
3926 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
3928 case 2: /* no issue time, expired */
3929 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
3930 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
3932 case 3: /* no issue time, revoked */
3933 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
3934 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
3936 case 4: /* issue time set, indefinite expiration */
3937 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3938 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
3940 case 5: /* issue time set, expires in future */
3941 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3942 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
3943 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3945 case 6: /* issue time set, expired */
3946 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3947 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
3948 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3950 case 7: /* issue time set, revoked */
3951 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3952 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
3953 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3956 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
3961 static CHANSERV_FUNC(cmd_info
)
3963 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
3964 struct userData
*uData
, *owner
;
3965 struct chanData
*cData
;
3966 struct do_not_register
*dnr
;
3971 cData
= channel
->channel_info
;
3972 reply("CSMSG_CHANNEL_INFO", channel
->name
);
3974 uData
= GetChannelUser(cData
, user
->handle_info
);
3975 if(uData
&& (uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
3977 mod_chanmode_format(&cData
->modes
, modes
);
3978 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
3979 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
3982 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
3986 note
= iter_data(it
);
3987 if(!note_type_visible_to_user(cData
, note
->type
, user
))
3990 padding
= PADLEN
- 1 - strlen(iter_key(it
));
3991 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
3994 reply("CSMSG_CHANNEL_MAX", cData
->max
);
3995 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
3996 if(owner
->access
== UL_OWNER
)
3997 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
3998 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
3999 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
4000 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4001 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4003 privileged
= IsStaff(user
);
4004 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4005 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4007 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4008 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4010 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4012 struct suspended
*suspended
;
4013 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4014 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4015 show_suspension_info(cmd
, user
, suspended
);
4017 else if(IsSuspended(cData
))
4019 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4020 show_suspension_info(cmd
, user
, cData
->suspended
);
4025 static CHANSERV_FUNC(cmd_netinfo
)
4027 extern time_t boot_time
;
4028 extern unsigned long burst_length
;
4029 char interval
[INTERVALLEN
];
4031 reply("CSMSG_NETWORK_INFO");
4032 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4033 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4034 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4035 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4036 reply("CSMSG_NETWORK_BANS", banCount
);
4037 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4038 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4039 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4044 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4046 struct helpfile_table table
;
4048 struct userNode
*user
;
4053 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4054 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4055 for(nn
=0; nn
<list
->used
; nn
++)
4057 user
= list
->list
[nn
];
4058 if(user
->modes
& skip_flags
)
4062 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4065 nick
= alloca(strlen(user
->nick
)+3);
4066 sprintf(nick
, "(%s)", user
->nick
);
4070 table
.contents
[table
.length
][0] = nick
;
4073 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4076 static CHANSERV_FUNC(cmd_ircops
)
4078 reply("CSMSG_STAFF_OPERS");
4079 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4083 static CHANSERV_FUNC(cmd_helpers
)
4085 reply("CSMSG_STAFF_HELPERS");
4086 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4090 static CHANSERV_FUNC(cmd_staff
)
4092 reply("CSMSG_NETWORK_STAFF");
4093 cmd_ircops(CSFUNC_ARGS
);
4094 cmd_helpers(CSFUNC_ARGS
);
4098 static CHANSERV_FUNC(cmd_peek
)
4100 struct modeNode
*mn
;
4101 char modes
[MODELEN
];
4103 struct helpfile_table table
;
4105 irc_make_chanmode(channel
, modes
);
4107 reply("CSMSG_PEEK_INFO", channel
->name
);
4108 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4109 reply("CSMSG_PEEK_MODES", modes
);
4110 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4114 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4115 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4116 for(n
= 0; n
< channel
->members
.used
; n
++)
4118 mn
= channel
->members
.list
[n
];
4119 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4121 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4122 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4127 reply("CSMSG_PEEK_OPS");
4128 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4131 reply("CSMSG_PEEK_NO_OPS");
4135 static MODCMD_FUNC(cmd_wipeinfo
)
4137 struct handle_info
*victim
;
4138 struct userData
*ud
, *actor
;
4141 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4142 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4144 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4146 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4149 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4151 reply("MSG_USER_OUTRANKED", victim
->handle
);
4157 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4161 static CHANSERV_FUNC(cmd_resync
)
4163 struct mod_chanmode
*changes
;
4164 struct chanData
*cData
= channel
->channel_info
;
4165 unsigned int ii
, used
;
4167 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4168 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4170 struct modeNode
*mn
= channel
->members
.list
[ii
];
4171 struct userData
*uData
;
4173 if(IsService(mn
->user
))
4176 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4177 if(!cData
->lvlOpts
[lvlGiveOps
]
4178 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4180 if(!(mn
->modes
& MODE_CHANOP
))
4182 changes
->args
[used
].mode
= MODE_CHANOP
;
4183 changes
->args
[used
++].u
.member
= mn
;
4186 else if(!cData
->lvlOpts
[lvlGiveHalfOps
]
4187 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
]))
4189 if(!(mn
->modes
& MODE_HALFOP
))
4191 changes
->args
[used
].mode
= MODE_HALFOP
;
4192 changes
->args
[used
++].u
.member
= mn
;
4194 if(mn
->modes
& MODE_CHANOP
)
4196 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4197 changes
->args
[used
++].u
.member
= mn
;
4199 if(mn
->modes
& MODE_VOICE
)
4201 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4202 changes
->args
[used
++].u
.member
= mn
;
4205 else if(!cData
->lvlOpts
[lvlGiveVoice
]
4206 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
]))
4208 if(mn
->modes
& MODE_CHANOP
)
4210 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4211 changes
->args
[used
++].u
.member
= mn
;
4213 if(mn
->modes
& MODE_HALFOP
)
4215 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4216 changes
->args
[used
++].u
.member
= mn
;
4218 if(!(mn
->modes
& MODE_VOICE
))
4220 changes
->args
[used
].mode
= MODE_VOICE
;
4221 changes
->args
[used
++].u
.member
= mn
;
4228 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4229 changes
->args
[used
++].u
.member
= mn
;
4233 changes
->argc
= used
;
4234 modcmd_chanmode_announce(changes
);
4235 mod_chanmode_free(changes
);
4236 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4240 static CHANSERV_FUNC(cmd_seen
)
4242 struct userData
*uData
;
4243 struct handle_info
*handle
;
4244 char seen
[INTERVALLEN
];
4248 if(!irccasecmp(argv
[1], chanserv
->nick
))
4250 reply("CSMSG_IS_CHANSERV");
4254 if(!(handle
= get_handle_info(argv
[1])))
4256 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4260 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4262 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4267 reply("CSMSG_USER_PRESENT", handle
->handle
);
4268 else if(uData
->seen
)
4269 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4271 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4273 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4274 reply("CSMSG_USER_VACATION", handle
->handle
);
4279 static MODCMD_FUNC(cmd_names
)
4281 struct userNode
*targ
;
4282 struct userData
*targData
;
4283 unsigned int ii
, pos
;
4286 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4288 targ
= channel
->members
.list
[ii
]->user
;
4289 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4292 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4295 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4299 if(IsUserSuspended(targData
))
4301 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4304 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4305 reply("CSMSG_END_NAMES", channel
->name
);
4310 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4312 switch(ntype
->visible_type
)
4314 case NOTE_VIS_ALL
: return 1;
4315 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4316 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4321 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4323 struct userData
*uData
;
4325 switch(ntype
->set_access_type
)
4327 case NOTE_SET_CHANNEL_ACCESS
:
4328 if(!user
->handle_info
)
4330 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4332 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4333 case NOTE_SET_CHANNEL_SETTER
:
4334 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4335 case NOTE_SET_PRIVILEGED
: default:
4336 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4340 static CHANSERV_FUNC(cmd_note
)
4342 struct chanData
*cData
;
4344 struct note_type
*ntype
;
4346 cData
= channel
->channel_info
;
4349 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4353 /* If no arguments, show all visible notes for the channel. */
4359 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4361 note
= iter_data(it
);
4362 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4365 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4366 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4369 reply("CSMSG_NOTELIST_END", channel
->name
);
4371 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4373 /* If one argument, show the named note. */
4376 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4377 && note_type_visible_to_user(cData
, note
->type
, user
))
4379 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4381 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4382 && note_type_visible_to_user(NULL
, ntype
, user
))
4384 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4389 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4393 /* Assume they're trying to set a note. */
4397 ntype
= dict_find(note_types
, argv
[1], NULL
);
4400 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4403 else if(note_type_settable_by_user(channel
, ntype
, user
))
4405 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4406 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4407 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4408 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4409 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4411 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4413 /* The note is viewable to staff only, so return 0
4414 to keep the invocation from getting logged (or
4415 regular users can see it in !events). */
4421 reply("CSMSG_NO_ACCESS");
4428 static CHANSERV_FUNC(cmd_delnote
)
4433 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4434 || !note_type_settable_by_user(channel
, note
->type
, user
))
4436 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4439 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4440 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4444 static CHANSERV_FUNC(cmd_events
)
4446 struct logSearch discrim
;
4447 struct logReport report
;
4448 unsigned int matches
, limit
;
4450 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4451 if(limit
< 1 || limit
> 200)
4454 memset(&discrim
, 0, sizeof(discrim
));
4455 discrim
.masks
.bot
= chanserv
;
4456 discrim
.masks
.channel_name
= channel
->name
;
4458 discrim
.masks
.command
= argv
[2];
4459 discrim
.limit
= limit
;
4460 discrim
.max_time
= INT_MAX
;
4461 discrim
.severities
= 1 << LOG_COMMAND
;
4462 report
.reporter
= chanserv
;
4464 reply("CSMSG_EVENT_SEARCH_RESULTS");
4465 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4467 reply("MSG_MATCH_COUNT", matches
);
4469 reply("MSG_NO_MATCHES");
4473 static CHANSERV_FUNC(cmd_say
)
4479 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4480 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4482 else if(GetUserH(argv
[1]))
4485 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4486 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4490 reply("MSG_NOT_TARGET_NAME");
4496 static CHANSERV_FUNC(cmd_emote
)
4502 /* CTCP is so annoying. */
4503 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4504 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4506 else if(GetUserH(argv
[1]))
4508 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4509 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4513 reply("MSG_NOT_TARGET_NAME");
4519 struct channelList
*
4520 chanserv_support_channels(void)
4522 return &chanserv_conf
.support_channels
;
4525 static CHANSERV_FUNC(cmd_expire
)
4527 int channel_count
= registered_channels
;
4528 expire_channels(NULL
);
4529 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4534 chanserv_expire_suspension(void *data
)
4536 struct suspended
*suspended
= data
;
4537 struct chanNode
*channel
;
4538 struct mod_chanmode change
;
4540 if(!suspended
->expires
|| (now
< suspended
->expires
))
4541 suspended
->revoked
= now
;
4542 channel
= suspended
->cData
->channel
;
4543 suspended
->cData
->channel
= channel
;
4544 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4545 mod_chanmode_init(&change
);
4547 change
.args
[0].mode
= MODE_CHANOP
;
4548 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4549 mod_chanmode_announce(chanserv
, channel
, &change
);
4552 static CHANSERV_FUNC(cmd_csuspend
)
4554 struct suspended
*suspended
;
4555 char reason
[MAXLEN
];
4556 time_t expiry
, duration
;
4557 struct userData
*uData
;
4561 if(IsProtected(channel
->channel_info
))
4563 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4567 if(argv
[1][0] == '!')
4569 else if(IsSuspended(channel
->channel_info
))
4571 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4572 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4576 if(!strcmp(argv
[1], "0"))
4578 else if((duration
= ParseInterval(argv
[1])))
4579 expiry
= now
+ duration
;
4582 reply("MSG_INVALID_DURATION", argv
[1]);
4586 unsplit_string(argv
+ 2, argc
- 2, reason
);
4588 suspended
= calloc(1, sizeof(*suspended
));
4589 suspended
->revoked
= 0;
4590 suspended
->issued
= now
;
4591 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4592 suspended
->expires
= expiry
;
4593 suspended
->reason
= strdup(reason
);
4594 suspended
->cData
= channel
->channel_info
;
4595 suspended
->previous
= suspended
->cData
->suspended
;
4596 suspended
->cData
->suspended
= suspended
;
4598 if(suspended
->expires
)
4599 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4601 if(IsSuspended(channel
->channel_info
))
4603 suspended
->previous
->revoked
= now
;
4604 if(suspended
->previous
->expires
)
4605 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4606 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4607 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4611 /* Mark all users in channel as absent. */
4612 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4621 /* Mark the channel as suspended, then part. */
4622 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4623 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4624 reply("CSMSG_SUSPENDED", channel
->name
);
4625 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4626 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4631 static CHANSERV_FUNC(cmd_cunsuspend
)
4633 struct suspended
*suspended
;
4634 char message
[MAXLEN
];
4636 if(!IsSuspended(channel
->channel_info
))
4638 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4642 suspended
= channel
->channel_info
->suspended
;
4644 /* Expire the suspension and join ChanServ to the channel. */
4645 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4646 chanserv_expire_suspension(suspended
);
4647 reply("CSMSG_UNSUSPENDED", channel
->name
);
4648 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4649 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4653 typedef struct chanservSearch
4661 unsigned long flags
;
4665 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4668 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4673 search
= malloc(sizeof(struct chanservSearch
));
4674 memset(search
, 0, sizeof(*search
));
4677 for(i
= 0; i
< argc
; i
++)
4679 /* Assume all criteria require arguments. */
4682 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4686 if(!irccasecmp(argv
[i
], "name"))
4687 search
->name
= argv
[++i
];
4688 else if(!irccasecmp(argv
[i
], "registrar"))
4689 search
->registrar
= argv
[++i
];
4690 else if(!irccasecmp(argv
[i
], "unvisited"))
4691 search
->unvisited
= ParseInterval(argv
[++i
]);
4692 else if(!irccasecmp(argv
[i
], "registered"))
4693 search
->registered
= ParseInterval(argv
[++i
]);
4694 else if(!irccasecmp(argv
[i
], "flags"))
4697 if(!irccasecmp(argv
[i
], "nodelete"))
4698 search
->flags
|= CHANNEL_NODELETE
;
4699 else if(!irccasecmp(argv
[i
], "suspended"))
4700 search
->flags
|= CHANNEL_SUSPENDED
;
4703 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4707 else if(!irccasecmp(argv
[i
], "limit"))
4708 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4711 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4716 if(search
->name
&& !strcmp(search
->name
, "*"))
4718 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4719 search
->registrar
= 0;
4728 chanserv_channel_match(struct chanData
*channel
, search_t search
)
4730 const char *name
= channel
->channel
->name
;
4731 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
4732 (search
->registrar
&& !channel
->registrar
) ||
4733 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
4734 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
4735 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
4736 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
4743 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
4745 struct chanData
*channel
;
4746 unsigned int matches
= 0;
4748 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
4750 if(!chanserv_channel_match(channel
, search
))
4760 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
4765 search_print(struct chanData
*channel
, void *data
)
4767 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
4770 static CHANSERV_FUNC(cmd_search
)
4773 unsigned int matches
;
4774 channel_search_func action
;
4778 if(!irccasecmp(argv
[1], "count"))
4779 action
= search_count
;
4780 else if(!irccasecmp(argv
[1], "print"))
4781 action
= search_print
;
4784 reply("CSMSG_ACTION_INVALID", argv
[1]);
4788 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
4792 if(action
== search_count
)
4793 search
->limit
= INT_MAX
;
4795 if(action
== search_print
)
4796 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4798 matches
= chanserv_channel_search(search
, action
, user
);
4801 reply("MSG_MATCH_COUNT", matches
);
4803 reply("MSG_NO_MATCHES");
4809 static CHANSERV_FUNC(cmd_unvisited
)
4811 struct chanData
*cData
;
4812 time_t interval
= chanserv_conf
.channel_expire_delay
;
4813 char buffer
[INTERVALLEN
];
4814 unsigned int limit
= 25, matches
= 0;
4818 interval
= ParseInterval(argv
[1]);
4820 limit
= atoi(argv
[2]);
4823 intervalString(buffer
, interval
, user
->handle_info
);
4824 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
4826 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
4828 if((now
- cData
->visited
) < interval
)
4831 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
4832 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
4839 static MODCMD_FUNC(chan_opt_defaulttopic
)
4845 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4847 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4851 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
4853 free(channel
->channel_info
->topic
);
4854 if(topic
[0] == '*' && topic
[1] == 0)
4856 topic
= channel
->channel_info
->topic
= NULL
;
4860 topic
= channel
->channel_info
->topic
= strdup(topic
);
4861 if(channel
->channel_info
->topic_mask
4862 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
4863 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4865 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
4868 if(channel
->channel_info
->topic
)
4869 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
4871 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
4875 static MODCMD_FUNC(chan_opt_topicmask
)
4879 struct chanData
*cData
= channel
->channel_info
;
4882 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4884 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4888 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
4890 if(cData
->topic_mask
)
4891 free(cData
->topic_mask
);
4892 if(mask
[0] == '*' && mask
[1] == 0)
4894 cData
->topic_mask
= 0;
4898 cData
->topic_mask
= strdup(mask
);
4900 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
4901 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
4902 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4906 if(channel
->channel_info
->topic_mask
)
4907 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
4909 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
4913 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
4917 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
4921 if(greeting
[0] == '*' && greeting
[1] == 0)
4925 unsigned int length
= strlen(greeting
);
4926 if(length
> chanserv_conf
.greeting_length
)
4928 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
4931 *data
= strdup(greeting
);
4940 reply(name
, user_find_message(user
, "MSG_NONE"));
4944 static MODCMD_FUNC(chan_opt_greeting
)
4946 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
4949 static MODCMD_FUNC(chan_opt_usergreeting
)
4951 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
4954 static MODCMD_FUNC(chan_opt_modes
)
4956 struct mod_chanmode
*new_modes
;
4957 char modes
[MODELEN
];
4961 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
4963 reply("CSMSG_NO_ACCESS");
4966 if(argv
[1][0] == '*' && argv
[1][1] == 0)
4968 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
4970 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
4972 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
4975 else if(new_modes
->argc
> 1)
4977 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
4978 mod_chanmode_free(new_modes
);
4983 channel
->channel_info
->modes
= *new_modes
;
4984 modcmd_chanmode_announce(new_modes
);
4985 mod_chanmode_free(new_modes
);
4989 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4991 reply("CSMSG_SET_MODES", modes
);
4993 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
4997 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4999 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5001 struct chanData
*cData
= channel
->channel_info
;
5006 /* Set flag according to value. */
5007 if(enabled_string(argv
[1]))
5009 cData
->flags
|= mask
;
5012 else if(disabled_string(argv
[1]))
5014 cData
->flags
&= ~mask
;
5019 reply("MSG_INVALID_BINARY", argv
[1]);
5025 /* Find current option value. */
5026 value
= (cData
->flags
& mask
) ? 1 : 0;
5030 reply(name
, user_find_message(user
, "MSG_ON"));
5032 reply(name
, user_find_message(user
, "MSG_OFF"));
5036 static MODCMD_FUNC(chan_opt_nodelete
)
5038 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5040 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5044 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5047 static MODCMD_FUNC(chan_opt_dynlimit
)
5049 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5052 static MODCMD_FUNC(chan_opt_offchannel
)
5054 struct chanData
*cData
= channel
->channel_info
;
5059 /* Set flag according to value. */
5060 if(enabled_string(argv
[1]))
5062 if(!IsOffChannel(cData
))
5063 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5064 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5067 else if(disabled_string(argv
[1]))
5069 if(IsOffChannel(cData
))
5071 struct mod_chanmode change
;
5072 mod_chanmode_init(&change
);
5074 change
.args
[0].mode
= MODE_CHANOP
;
5075 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5076 mod_chanmode_announce(chanserv
, channel
, &change
);
5078 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5083 reply("MSG_INVALID_BINARY", argv
[1]);
5089 /* Find current option value. */
5090 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5094 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5096 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5100 static MODCMD_FUNC(chan_opt_defaults
)
5102 struct userData
*uData
;
5103 struct chanData
*cData
;
5104 const char *confirm
;
5105 enum levelOption lvlOpt
;
5106 enum charOption chOpt
;
5108 cData
= channel
->channel_info
;
5109 uData
= GetChannelUser(cData
, user
->handle_info
);
5110 if(!uData
|| (uData
->access
< UL_OWNER
))
5112 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5115 confirm
= make_confirmation_string(uData
);
5116 if((argc
< 2) || strcmp(argv
[1], confirm
))
5118 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5121 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5122 cData
->modes
= chanserv_conf
.default_modes
;
5123 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5124 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5125 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5126 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5127 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5132 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5134 struct chanData
*cData
= channel
->channel_info
;
5135 struct userData
*uData
;
5136 unsigned short value
;
5140 if(!check_user_level(channel
, user
, option
, 1, 1))
5142 reply("CSMSG_CANNOT_SET");
5145 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5146 if(!value
&& strcmp(argv
[1], "0"))
5148 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5151 uData
= GetChannelUser(cData
, user
->handle_info
);
5152 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5154 reply("CSMSG_BAD_SETLEVEL");
5160 if(value
> cData
->lvlOpts
[lvlGiveOps
])
5162 reply("CSMSG_BAD_GIVEVOICE", cData
->lvlOpts
[lvlGiveOps
]);
5166 case lvlGiveHalfOps
:
5167 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5169 reply("CSMSG_BAD_GIVEHOPS", cData
->lvlOpts
[lvlGiveHalfOps
]);
5174 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5176 reply("CSMSG_BAD_GIVEOPS", cData
->lvlOpts
[lvlGiveVoice
]);
5181 /* This test only applies to owners, since non-owners
5182 * trying to set an option to above their level get caught
5183 * by the CSMSG_BAD_SETLEVEL test above.
5185 if(value
> uData
->access
)
5187 reply("CSMSG_BAD_SETTERS");
5194 cData
->lvlOpts
[option
] = value
;
5196 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5200 static MODCMD_FUNC(chan_opt_enfops
)
5202 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5205 static MODCMD_FUNC(chan_opt_enfhalfops
)
5207 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5210 static MODCMD_FUNC(chan_opt_giveops
)
5212 return channel_level_option(lvlGiveOps
, CSFUNC_ARGS
);
5215 static MODCMD_FUNC(chan_opt_givehalfops
)
5217 return channel_level_option(lvlGiveHalfOps
, CSFUNC_ARGS
);
5220 static MODCMD_FUNC(chan_opt_enfmodes
)
5222 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5225 static MODCMD_FUNC(chan_opt_enftopic
)
5227 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5230 static MODCMD_FUNC(chan_opt_pubcmd
)
5232 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5235 static MODCMD_FUNC(chan_opt_setters
)
5237 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5240 static MODCMD_FUNC(chan_opt_ctcpusers
)
5242 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5245 static MODCMD_FUNC(chan_opt_userinfo
)
5247 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5250 static MODCMD_FUNC(chan_opt_givevoice
)
5252 return channel_level_option(lvlGiveVoice
, CSFUNC_ARGS
);
5255 static MODCMD_FUNC(chan_opt_topicsnarf
)
5257 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5260 static MODCMD_FUNC(chan_opt_inviteme
)
5262 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5266 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5268 struct chanData
*cData
= channel
->channel_info
;
5269 int count
= charOptions
[option
].count
, index
;
5273 index
= atoi(argv
[1]);
5275 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5277 reply("CSMSG_INVALID_NUMERIC", index
);
5278 /* Show possible values. */
5279 for(index
= 0; index
< count
; index
++)
5280 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5284 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5288 /* Find current option value. */
5291 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5295 /* Somehow, the option value is corrupt; reset it to the default. */
5296 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5301 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5305 static MODCMD_FUNC(chan_opt_protect
)
5307 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5310 static MODCMD_FUNC(chan_opt_toys
)
5312 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5315 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5317 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5320 static MODCMD_FUNC(chan_opt_topicrefresh
)
5322 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5325 static struct svccmd_list set_shows_list
;
5328 handle_svccmd_unbind(struct svccmd
*target
) {
5330 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5331 if(target
== set_shows_list
.list
[ii
])
5332 set_shows_list
.used
= 0;
5335 static CHANSERV_FUNC(cmd_set
)
5337 struct svccmd
*subcmd
;
5341 /* Check if we need to (re-)initialize set_shows_list. */
5342 if(!set_shows_list
.used
)
5344 if(!set_shows_list
.size
)
5346 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5347 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5349 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5351 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5352 sprintf(buf
, "%s %s", argv
[0], name
);
5353 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5356 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5359 svccmd_list_append(&set_shows_list
, subcmd
);
5365 reply("CSMSG_CHANNEL_OPTIONS");
5366 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5368 subcmd
= set_shows_list
.list
[ii
];
5369 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5374 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5375 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5378 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5381 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5383 reply("CSMSG_NO_ACCESS");
5387 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5391 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5393 struct userData
*uData
;
5395 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5398 reply("CSMSG_NOT_USER", channel
->name
);
5404 /* Just show current option value. */
5406 else if(enabled_string(argv
[1]))
5408 uData
->flags
|= mask
;
5410 else if(disabled_string(argv
[1]))
5412 uData
->flags
&= ~mask
;
5416 reply("MSG_INVALID_BINARY", argv
[1]);
5420 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5424 static MODCMD_FUNC(user_opt_noautoop
)
5426 struct userData
*uData
;
5428 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5431 reply("CSMSG_NOT_USER", channel
->name
);
5434 if(uData
->access
< channel
->channel_info
->lvlOpts
[lvlGiveOps
])
5435 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5437 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5440 static MODCMD_FUNC(user_opt_autoinvite
)
5442 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5445 static MODCMD_FUNC(user_opt_info
)
5447 struct userData
*uData
;
5450 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5454 /* If they got past the command restrictions (which require access)
5455 * but fail this test, we have some fool with security override on.
5457 reply("CSMSG_NOT_USER", channel
->name
);
5464 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5465 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5467 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5470 bp
= strcspn(infoline
, "\001");
5473 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5478 if(infoline
[0] == '*' && infoline
[1] == 0)
5481 uData
->info
= strdup(infoline
);
5484 reply("CSMSG_USET_INFO", uData
->info
);
5486 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5490 struct svccmd_list uset_shows_list
;
5492 static CHANSERV_FUNC(cmd_uset
)
5494 struct svccmd
*subcmd
;
5498 /* Check if we need to (re-)initialize uset_shows_list. */
5499 if(!uset_shows_list
.used
)
5503 "NoAutoOp", "AutoInvite", "Info"
5506 if(!uset_shows_list
.size
)
5508 uset_shows_list
.size
= ArrayLength(options
);
5509 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5511 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5513 const char *name
= options
[ii
];
5514 sprintf(buf
, "%s %s", argv
[0], name
);
5515 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5518 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5521 svccmd_list_append(&uset_shows_list
, subcmd
);
5527 /* Do this so options are presented in a consistent order. */
5528 reply("CSMSG_USER_OPTIONS");
5529 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5530 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5534 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5535 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5538 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5542 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5545 static CHANSERV_FUNC(cmd_giveownership
)
5547 struct handle_info
*new_owner_hi
;
5548 struct userData
*new_owner
, *curr_user
;
5549 struct chanData
*cData
= channel
->channel_info
;
5550 struct do_not_register
*dnr
;
5552 unsigned short co_access
;
5553 char reason
[MAXLEN
];
5556 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5557 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5558 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5560 struct userData
*owner
= NULL
;
5561 for(curr_user
= channel
->channel_info
->users
;
5563 curr_user
= curr_user
->next
)
5565 if(curr_user
->access
!= UL_OWNER
)
5569 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5576 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5578 char delay
[INTERVALLEN
];
5579 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5580 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5583 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5585 if(new_owner_hi
== user
->handle_info
)
5587 reply("CSMSG_NO_TRANSFER_SELF");
5590 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5593 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5596 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5598 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5601 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5602 if(!IsHelping(user
))
5603 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5605 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
);
5608 if(new_owner
->access
>= UL_COOWNER
)
5609 co_access
= new_owner
->access
;
5611 co_access
= UL_COOWNER
;
5612 new_owner
->access
= UL_OWNER
;
5614 curr_user
->access
= co_access
;
5615 cData
->ownerTransfer
= now
;
5616 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5617 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5618 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5622 static CHANSERV_FUNC(cmd_suspend
)
5624 struct handle_info
*hi
;
5625 struct userData
*self
, *target
;
5628 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5629 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5630 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5632 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5635 if(target
->access
>= self
->access
)
5637 reply("MSG_USER_OUTRANKED", hi
->handle
);
5640 if(target
->flags
& USER_SUSPENDED
)
5642 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5647 target
->present
= 0;
5650 target
->flags
|= USER_SUSPENDED
;
5651 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5655 static CHANSERV_FUNC(cmd_unsuspend
)
5657 struct handle_info
*hi
;
5658 struct userData
*self
, *target
;
5661 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5662 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5663 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5665 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5668 if(target
->access
>= self
->access
)
5670 reply("MSG_USER_OUTRANKED", hi
->handle
);
5673 if(!(target
->flags
& USER_SUSPENDED
))
5675 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5678 target
->flags
&= ~USER_SUSPENDED
;
5679 scan_user_presence(target
, NULL
);
5680 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5684 static MODCMD_FUNC(cmd_deleteme
)
5686 struct handle_info
*hi
;
5687 struct userData
*target
;
5688 const char *confirm_string
;
5689 unsigned short access
;
5692 hi
= user
->handle_info
;
5693 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5695 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5698 if(target
->access
== UL_OWNER
)
5700 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5703 confirm_string
= make_confirmation_string(target
);
5704 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
5706 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
5709 access
= target
->access
;
5710 channel_name
= strdup(channel
->name
);
5711 del_channel_user(target
, 1);
5712 reply("CSMSG_DELETED_YOU", access
, channel_name
);
5718 chanserv_refresh_topics(UNUSED_ARG(void *data
))
5720 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
5721 struct chanData
*cData
;
5724 for(cData
= channelList
; cData
; cData
= cData
->next
)
5726 if(IsSuspended(cData
))
5728 opt
= cData
->chOpts
[chTopicRefresh
];
5731 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
5734 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
5735 cData
->last_refresh
= refresh_num
;
5737 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
5740 static CHANSERV_FUNC(cmd_unf
)
5744 char response
[MAXLEN
];
5745 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
5746 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5747 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5750 reply("CSMSG_UNF_RESPONSE");
5754 static CHANSERV_FUNC(cmd_ping
)
5758 char response
[MAXLEN
];
5759 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
5760 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5761 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5764 reply("CSMSG_PING_RESPONSE");
5768 static CHANSERV_FUNC(cmd_wut
)
5772 char response
[MAXLEN
];
5773 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
5774 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5775 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5778 reply("CSMSG_WUT_RESPONSE");
5782 static CHANSERV_FUNC(cmd_8ball
)
5784 unsigned int i
, j
, accum
;
5789 for(i
=1; i
<argc
; i
++)
5790 for(j
=0; argv
[i
][j
]; j
++)
5791 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
5792 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
5795 char response
[MAXLEN
];
5796 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
5797 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5800 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
5804 static CHANSERV_FUNC(cmd_d
)
5806 unsigned long sides
, count
, modifier
, ii
, total
;
5807 char response
[MAXLEN
], *sep
;
5811 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
5821 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
5822 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
5826 else if((sep
[0] == '-') && isdigit(sep
[1]))
5827 modifier
= strtoul(sep
, NULL
, 10);
5828 else if((sep
[0] == '+') && isdigit(sep
[1]))
5829 modifier
= strtoul(sep
+1, NULL
, 10);
5836 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
5841 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
5844 for(total
= ii
= 0; ii
< count
; ++ii
)
5845 total
+= (rand() % sides
) + 1;
5848 if((count
> 1) || modifier
)
5850 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
5851 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
5855 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
5856 sprintf(response
, fmt
, total
, sides
);
5859 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5861 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5865 static CHANSERV_FUNC(cmd_huggle
)
5867 /* CTCP must be via PRIVMSG, never notice */
5869 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
5871 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
5875 static CHANSERV_FUNC(cmd_calc
)
5877 char response
[MAXLEN
];
5880 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
5883 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5885 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5890 chanserv_adjust_limit(void *data
)
5892 struct mod_chanmode change
;
5893 struct chanData
*cData
= data
;
5894 struct chanNode
*channel
= cData
->channel
;
5897 if(IsSuspended(cData
))
5900 cData
->limitAdjusted
= now
;
5901 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
5902 if(cData
->modes
.modes_set
& MODE_LIMIT
)
5904 if(limit
> cData
->modes
.new_limit
)
5905 limit
= cData
->modes
.new_limit
;
5906 else if(limit
== cData
->modes
.new_limit
)
5910 mod_chanmode_init(&change
);
5911 change
.modes_set
= MODE_LIMIT
;
5912 change
.new_limit
= limit
;
5913 mod_chanmode_announce(chanserv
, channel
, &change
);
5917 handle_new_channel(struct chanNode
*channel
)
5919 struct chanData
*cData
;
5921 if(!(cData
= channel
->channel_info
))
5924 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
5925 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
5927 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
5928 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
5931 /* Welcome to my worst nightmare. Warning: Read (or modify)
5932 the code below at your own risk. */
5934 handle_join(struct modeNode
*mNode
)
5936 struct mod_chanmode change
;
5937 struct userNode
*user
= mNode
->user
;
5938 struct chanNode
*channel
= mNode
->channel
;
5939 struct chanData
*cData
;
5940 struct userData
*uData
= NULL
;
5941 struct banData
*bData
;
5942 struct handle_info
*handle
;
5943 unsigned int modes
= 0, info
= 0;
5946 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
5949 cData
= channel
->channel_info
;
5950 if(channel
->members
.used
> cData
->max
)
5951 cData
->max
= channel
->members
.used
;
5953 /* Check for bans. If they're joining through a ban, one of two
5955 * 1: Join during a netburst, by riding the break. Kick them
5956 * unless they have ops or voice in the channel.
5957 * 2: They're allowed to join through the ban (an invite in
5958 * ircu2.10, or a +e on Hybrid, or something).
5959 * If they're not joining through a ban, and the banlist is not
5960 * full, see if they're on the banlist for the channel. If so,
5963 /* This is really, really stupid. not all banned people are kicked.
5964 * sometimes we like to leave them unkicked.
5965 * I tried to explain this to the srvx developers and
5966 * got insulted.. hence one reason for this fork.
5968 if(user->uplink->burst && !mNode->modes)
5971 for(ii = 0; ii < channel->banlist.used; ii++)
5973 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5975 ** Riding a netburst. Naughty. **
5976 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5983 mod_chanmode_init(&change
);
5985 if(channel
->banlist
.used
< MAXBANS
)
5987 /* Not joining through a ban. */
5988 for(bData
= cData
->bans
;
5989 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
5990 bData
= bData
->next
);
5994 char kick_reason
[MAXLEN
];
5995 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
5997 bData
->triggered
= now
;
5998 if(bData
!= cData
->bans
)
6000 /* Shuffle the ban to the head of the list. */
6002 bData
->next
->prev
= bData
->prev
;
6004 bData
->prev
->next
= bData
->next
;
6007 bData
->next
= cData
->bans
;
6010 cData
->bans
->prev
= bData
;
6011 cData
->bans
= bData
;
6014 change
.args
[0].mode
= MODE_BAN
;
6015 change
.args
[0].u
.hostmask
= bData
->mask
;
6016 mod_chanmode_announce(chanserv
, channel
, &change
);
6017 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6022 /* ChanServ will not modify the limits in join-flooded channels.
6023 It will also skip DynLimit processing when the user (or srvx)
6024 is bursting in, because there are likely more incoming. */
6025 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6026 && !user
->uplink
->burst
6027 && !channel
->join_flooded
6028 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6030 /* The user count has begun "bumping" into the channel limit,
6031 so set a timer to raise the limit a bit. Any previous
6032 timers are removed so three incoming users within the delay
6033 results in one limit change, not three. */
6035 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6036 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6039 if(channel
->join_flooded
)
6041 /* don't automatically give ops or voice during a join flood */
6043 else if(cData
->lvlOpts
[lvlGiveOps
] == 0)
6044 modes
|= MODE_CHANOP
;
6045 else if(cData
->lvlOpts
[lvlGiveHalfOps
] == 0)
6046 modes
|= MODE_HALFOP
;
6047 else if(cData
->lvlOpts
[lvlGiveVoice
] == 0)
6048 modes
|= MODE_VOICE
;
6050 greeting
= cData
->greeting
;
6051 if(user
->handle_info
)
6053 handle
= user
->handle_info
;
6055 if(IsHelper(user
) && !IsHelping(user
))
6058 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6060 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6062 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6068 uData
= GetTrueChannelAccess(cData
, handle
);
6069 if(uData
&& !IsUserSuspended(uData
))
6071 /* Ops and above were handled by the above case. */
6072 if(IsUserAutoOp(uData
))
6074 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
6075 modes
|= MODE_CHANOP
;
6076 if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
6077 modes
|= MODE_HALFOP
;
6078 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
6079 modes
|= MODE_VOICE
;
6081 if(uData
->access
>= UL_PRESENT
)
6082 cData
->visited
= now
;
6083 if(cData
->user_greeting
)
6084 greeting
= cData
->user_greeting
;
6086 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6087 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6094 if(!user
->uplink
->burst
)
6098 if(modes
& MODE_CHANOP
) {
6099 modes
&= ~MODE_HALFOP
;
6100 modes
&= ~MODE_VOICE
;
6102 change
.args
[0].mode
= modes
;
6103 change
.args
[0].u
.member
= mNode
;
6104 mod_chanmode_announce(chanserv
, channel
, &change
);
6106 if(greeting
&& !user
->uplink
->burst
)
6107 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6109 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6115 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6117 struct mod_chanmode change
;
6118 struct userData
*channel
;
6119 unsigned int ii
, jj
;
6121 if(!user
->handle_info
)
6124 mod_chanmode_init(&change
);
6126 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6128 struct chanNode
*cn
;
6129 struct modeNode
*mn
;
6130 if(IsUserSuspended(channel
)
6131 || IsSuspended(channel
->channel
)
6132 || !(cn
= channel
->channel
->channel
))
6135 mn
= GetUserMode(cn
, user
);
6138 if(!IsUserSuspended(channel
)
6139 && IsUserAutoInvite(channel
)
6140 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6142 && !user
->uplink
->burst
)
6143 irc_invite(chanserv
, user
, cn
);
6147 if(channel
->access
>= UL_PRESENT
)
6148 channel
->channel
->visited
= now
;
6150 if(IsUserAutoOp(channel
))
6152 if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveOps
])
6153 change
.args
[0].mode
= MODE_CHANOP
;
6154 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
6155 change
.args
[0].mode
= MODE_HALFOP
;
6156 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveVoice
])
6157 change
.args
[0].mode
= MODE_VOICE
;
6159 change
.args
[0].mode
= 0;
6160 change
.args
[0].u
.member
= mn
;
6161 if(change
.args
[0].mode
)
6162 mod_chanmode_announce(chanserv
, cn
, &change
);
6165 channel
->seen
= now
;
6166 channel
->present
= 1;
6169 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6171 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6172 struct banData
*ban
;
6174 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6175 || !channel
->channel_info
)
6177 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6178 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6180 if(jj
< channel
->banlist
.used
)
6182 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6184 char kick_reason
[MAXLEN
];
6185 if(!user_matches_glob(user
, ban
->mask
, 1))
6187 change
.args
[0].mode
= MODE_BAN
;
6188 change
.args
[0].u
.hostmask
= ban
->mask
;
6189 mod_chanmode_announce(chanserv
, channel
, &change
);
6190 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6191 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6192 ban
->triggered
= now
;
6197 if(IsSupportHelper(user
))
6199 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6201 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6203 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6211 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6213 struct chanData
*cData
;
6214 struct userData
*uData
;
6216 cData
= mn
->channel
->channel_info
;
6217 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6220 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6222 /* Allow for a bit of padding so that the limit doesn't
6223 track the user count exactly, which could get annoying. */
6224 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6226 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6227 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6231 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6233 scan_user_presence(uData
, mn
->user
);
6237 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6239 unsigned int ii
, jj
;
6240 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6242 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6243 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6245 if(jj
< mn
->user
->channels
.used
)
6248 if(ii
== chanserv_conf
.support_channels
.used
)
6249 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6254 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6256 struct userData
*uData
;
6258 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6259 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6260 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6263 if(protect_user(victim
, kicker
, channel
->channel_info
))
6265 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6266 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6269 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6274 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6276 struct chanData
*cData
;
6278 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6281 cData
= channel
->channel_info
;
6282 if(bad_topic(channel
, user
, channel
->topic
))
6284 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6285 if(cData
->topic_mask
&& match_ircglob(old_topic
, cData
->topic_mask
))
6286 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6287 else if(cData
->topic
)
6288 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
6291 /* With topicsnarf, grab the topic and save it as the default topic. */
6292 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6295 cData
->topic
= strdup(channel
->topic
);
6301 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6303 struct mod_chanmode
*bounce
= NULL
;
6304 unsigned int bnc
, ii
;
6307 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6310 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6311 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6313 char correct
[MAXLEN
];
6314 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6315 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6316 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6318 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6320 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6322 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6323 if(!protect_user(victim
, user
, channel
->channel_info
))
6326 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6329 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6330 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6331 if(bounce
->args
[bnc
].u
.member
)
6335 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6336 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6338 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6340 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6342 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6343 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6346 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6347 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6348 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6351 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6353 const char *ban
= change
->args
[ii
].u
.hostmask
;
6354 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6357 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6358 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6359 bounce
->args
[bnc
].u
.hostmask
= ban
;
6361 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6366 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6367 mod_chanmode_announce(chanserv
, channel
, bounce
);
6368 mod_chanmode_free(bounce
);
6373 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6375 struct chanNode
*channel
;
6376 struct banData
*bData
;
6377 struct mod_chanmode change
;
6378 unsigned int ii
, jj
;
6379 char kick_reason
[MAXLEN
];
6381 mod_chanmode_init(&change
);
6383 change
.args
[0].mode
= MODE_BAN
;
6384 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6386 channel
= user
->channels
.list
[ii
]->channel
;
6387 /* Need not check for bans if they're opped or voiced. */
6388 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6390 /* Need not check for bans unless channel registration is active. */
6391 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6393 /* Look for a matching ban already on the channel. */
6394 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6395 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6397 /* Need not act if we found one. */
6398 if(jj
< channel
->banlist
.used
)
6400 /* Look for a matching ban in this channel. */
6401 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6403 if(!user_matches_glob(user
, bData
->mask
, 1))
6405 change
.args
[0].u
.hostmask
= bData
->mask
;
6406 mod_chanmode_announce(chanserv
, channel
, &change
);
6407 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6408 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6409 bData
->triggered
= now
;
6410 break; /* we don't need to check any more bans in the channel */
6415 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6417 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6421 dict_remove2(handle_dnrs
, old_handle
, 1);
6422 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6423 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6428 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6430 struct userNode
*h_user
;
6432 if(handle
->channels
)
6434 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6435 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6437 while(handle
->channels
)
6438 del_channel_user(handle
->channels
, 1);
6443 handle_server_link(UNUSED_ARG(struct server
*server
))
6445 struct chanData
*cData
;
6447 for(cData
= channelList
; cData
; cData
= cData
->next
)
6449 if(!IsSuspended(cData
))
6450 cData
->may_opchan
= 1;
6451 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6452 && !cData
->channel
->join_flooded
6453 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6454 < chanserv_conf
.adjust_threshold
))
6456 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6457 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6463 chanserv_conf_read(void)
6467 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6468 struct mod_chanmode
*change
;
6469 struct string_list
*strlist
;
6470 struct chanNode
*chan
;
6473 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6475 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6478 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6479 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6480 chanserv_conf
.support_channels
.used
= 0;
6481 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6483 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6485 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6488 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6490 channelList_append(&chanserv_conf
.support_channels
, chan
);
6493 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6496 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6499 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6501 channelList_append(&chanserv_conf
.support_channels
, chan
);
6503 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6504 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6505 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6506 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6507 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6508 chanserv_conf
.greeting_length
= str
? atoi(str
) : 120;
6509 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6510 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6511 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6512 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6513 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6514 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6515 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6516 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6517 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6518 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6519 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6520 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6521 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6522 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6523 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6524 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6525 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6527 NickChange(chanserv
, str
, 0);
6528 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6529 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6530 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6531 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6532 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6533 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6534 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6535 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6536 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6537 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6538 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6539 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6540 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6541 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6542 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6543 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6544 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6547 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6548 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6549 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6551 chanserv_conf
.default_modes
= *change
;
6552 mod_chanmode_free(change
);
6554 free_string_list(chanserv_conf
.set_shows
);
6555 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6557 strlist
= string_list_copy(strlist
);
6560 static const char *list
[] = {
6561 /* free form text */
6562 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6563 /* options based on user level */
6564 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveHalfOps", "GiveOps", "EnfOps",
6565 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6566 /* multiple choice options */
6567 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6568 /* binary options */
6569 "DynLimit", "NoDelete",
6574 strlist
= alloc_string_list(ArrayLength(list
)-1);
6575 for(ii
=0; list
[ii
]; ii
++)
6576 string_list_append(strlist
, strdup(list
[ii
]));
6578 chanserv_conf
.set_shows
= strlist
;
6579 /* We don't look things up now, in case the list refers to options
6580 * defined by modules initialized after this point. Just mark the
6581 * function list as invalid, so it will be initialized.
6583 set_shows_list
.used
= 0;
6584 free_string_list(chanserv_conf
.eightball
);
6585 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6588 strlist
= string_list_copy(strlist
);
6592 strlist
= alloc_string_list(4);
6593 string_list_append(strlist
, strdup("Yes."));
6594 string_list_append(strlist
, strdup("No."));
6595 string_list_append(strlist
, strdup("Maybe so."));
6597 chanserv_conf
.eightball
= strlist
;
6598 free_string_list(chanserv_conf
.old_ban_names
);
6599 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6601 strlist
= string_list_copy(strlist
);
6603 strlist
= alloc_string_list(2);
6604 chanserv_conf
.old_ban_names
= strlist
;
6605 /* the variable itself is actually declared in proto-common.c; this is equally
6607 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6608 off_channel
= (str
&& enabled_string(str
)) ? 1 : 0;
6609 str
= database_get_data(conf_node
, "use_registered_mode", RECDB_QSTRING
);
6610 chanserv_conf
.use_registered_mode
= (str
&& enabled_string(str
)) ? 1 : 0;
6614 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6617 struct note_type
*ntype
;
6620 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6622 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6625 if(!(ntype
= chanserv_create_note_type(key
)))
6627 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6631 /* Figure out set access */
6632 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6634 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6635 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6637 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6639 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6640 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6642 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6644 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6648 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6649 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6650 ntype
->set_access
.min_opserv
= 0;
6653 /* Figure out visibility */
6654 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6655 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6656 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6657 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6658 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6659 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6660 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6661 ntype
->visible_type
= NOTE_VIS_ALL
;
6663 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6665 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6666 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6670 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6672 struct handle_info
*handle
;
6673 struct userData
*uData
;
6674 char *seen
, *inf
, *flags
;
6676 unsigned short access
;
6678 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6680 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6684 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6685 if(access
> UL_OWNER
)
6687 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6691 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
6692 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
6693 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
6694 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
6695 handle
= get_handle_info(key
);
6698 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
6702 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
6703 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
6707 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6709 struct banData
*bData
;
6710 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
6711 time_t set_time
, triggered_time
, expires_time
;
6713 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6715 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
6719 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
6720 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
6721 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
6722 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
6723 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
6724 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
6726 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
6727 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
6729 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
6731 expires_time
= set_time
+ atoi(s_duration
);
6735 if(expires_time
&& (expires_time
< now
))
6738 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
6741 static struct suspended
*
6742 chanserv_read_suspended(dict_t obj
)
6744 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
6748 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
6749 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6750 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
6751 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6752 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
6753 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6754 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
6755 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
6756 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
6757 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
6762 chanserv_channel_read(const char *key
, struct record_data
*hir
)
6764 struct suspended
*suspended
;
6765 struct mod_chanmode
*modes
;
6766 struct chanNode
*cNode
;
6767 struct chanData
*cData
;
6768 struct dict
*channel
, *obj
;
6769 char *str
, *argv
[10];
6773 channel
= hir
->d
.object
;
6775 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
6778 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
6781 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
6784 cData
= register_channel(cNode
, str
);
6787 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
6791 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
6793 enum levelOption lvlOpt
;
6794 enum charOption chOpt
;
6796 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
6797 cData
->flags
= atoi(str
);
6799 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6801 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
6803 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
6804 else if(levelOptions
[lvlOpt
].old_flag
)
6806 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6807 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
6809 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6813 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6815 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
6817 cData
->chOpts
[chOpt
] = str
[0];
6820 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
6822 enum levelOption lvlOpt
;
6823 enum charOption chOpt
;
6826 cData
->flags
= base64toint(str
, 5);
6827 count
= strlen(str
+= 5);
6828 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6831 if(levelOptions
[lvlOpt
].old_flag
)
6833 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6834 lvl
= levelOptions
[lvlOpt
].flag_value
;
6836 lvl
= levelOptions
[lvlOpt
].default_value
;
6838 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
6840 case 'c': lvl
= UL_COOWNER
; break;
6841 case 'm': lvl
= UL_MANAGER
; break;
6842 case 'n': lvl
= UL_OWNER
+1; break;
6843 case 'o': lvl
= UL_OP
; break;
6844 case 'p': lvl
= UL_PEON
; break;
6845 case 'h': lvl
= UL_HALFOP
; break;
6846 case 'w': lvl
= UL_OWNER
; break;
6847 default: lvl
= 0; break;
6849 cData
->lvlOpts
[lvlOpt
] = lvl
;
6851 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6852 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
6855 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
6857 suspended
= chanserv_read_suspended(obj
);
6858 cData
->suspended
= suspended
;
6859 suspended
->cData
= cData
;
6860 /* We could use suspended->expires and suspended->revoked to
6861 * set the CHANNEL_SUSPENDED flag, but we don't. */
6863 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
6865 suspended
= calloc(1, sizeof(*suspended
));
6866 suspended
->issued
= 0;
6867 suspended
->revoked
= 0;
6868 suspended
->suspender
= strdup(str
);
6869 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
6870 suspended
->expires
= str
? atoi(str
) : 0;
6871 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
6872 suspended
->reason
= strdup(str
? str
: "No reason");
6873 suspended
->previous
= NULL
;
6874 cData
->suspended
= suspended
;
6875 suspended
->cData
= cData
;
6879 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6880 suspended
= NULL
; /* to squelch a warning */
6883 if(IsSuspended(cData
)) {
6884 if(suspended
->expires
> now
)
6885 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6886 else if(suspended
->expires
)
6887 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6890 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
6891 struct mod_chanmode change
;
6892 mod_chanmode_init(&change
);
6894 change
.args
[0].mode
= MODE_CHANOP
;
6895 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
6896 mod_chanmode_announce(chanserv
, cNode
, &change
);
6899 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
6900 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6901 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
6902 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6903 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
6904 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6905 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
6906 cData
->max
= str
? atoi(str
) : 0;
6907 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
6908 cData
->greeting
= str
? strdup(str
) : NULL
;
6909 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
6910 cData
->user_greeting
= str
? strdup(str
) : NULL
;
6911 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
6912 cData
->topic_mask
= str
? strdup(str
) : NULL
;
6913 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
6914 cData
->topic
= str
? strdup(str
) : NULL
;
6916 if(!IsSuspended(cData
)
6917 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
6918 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
6919 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
6920 cData
->modes
= *modes
;
6921 if(chanserv_conf
.use_registered_mode
)
6922 cData
->modes
.modes_set
|= MODE_REGISTERED
;
6923 if(cData
->modes
.argc
> 1)
6924 cData
->modes
.argc
= 1;
6925 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
6926 mod_chanmode_free(modes
);
6929 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
6930 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6931 user_read_helper(iter_key(it
), iter_data(it
), cData
);
6933 if(!cData
->users
&& !IsProtected(cData
))
6935 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
6936 unregister_channel(cData
, "has empty user list.");
6940 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
6941 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6942 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
6944 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
6945 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6947 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
6948 struct record_data
*rd
= iter_data(it
);
6949 const char *note
, *setter
;
6951 if(rd
->type
!= RECDB_OBJECT
)
6953 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
6957 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
6959 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
6961 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
6965 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
6966 if(!setter
) setter
= "<unknown>";
6967 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
6975 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
6977 const char *setter
, *reason
, *str
;
6978 struct do_not_register
*dnr
;
6980 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
6983 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
6986 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
6989 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
6992 dnr
= chanserv_add_dnr(key
, setter
, reason
);
6995 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
6997 dnr
->set
= atoi(str
);
7003 chanserv_saxdb_read(struct dict
*database
)
7005 struct dict
*section
;
7008 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7009 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7010 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7012 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7013 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7014 chanserv_channel_read(iter_key(it
), iter_data(it
));
7016 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7017 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7018 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7024 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7026 int high_present
= 0;
7027 saxdb_start_record(ctx
, KEY_USERS
, 1);
7028 for(; uData
; uData
= uData
->next
)
7030 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7032 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7033 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7034 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7036 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7038 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7039 saxdb_end_record(ctx
);
7041 saxdb_end_record(ctx
);
7042 return high_present
;
7046 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7050 saxdb_start_record(ctx
, KEY_BANS
, 1);
7051 for(; bData
; bData
= bData
->next
)
7053 saxdb_start_record(ctx
, bData
->mask
, 0);
7054 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7055 if(bData
->triggered
)
7056 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7058 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7060 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7062 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7063 saxdb_end_record(ctx
);
7065 saxdb_end_record(ctx
);
7069 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7071 saxdb_start_record(ctx
, name
, 0);
7072 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7073 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7075 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7077 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7079 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7081 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7082 saxdb_end_record(ctx
);
7086 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7090 enum levelOption lvlOpt
;
7091 enum charOption chOpt
;
7093 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7095 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7096 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7098 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7099 if(channel
->registrar
)
7100 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7101 if(channel
->greeting
)
7102 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7103 if(channel
->user_greeting
)
7104 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7105 if(channel
->topic_mask
)
7106 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7107 if(channel
->suspended
)
7108 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7110 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7111 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7112 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7113 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7114 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7116 buf
[0] = channel
->chOpts
[chOpt
];
7118 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7120 saxdb_end_record(ctx
);
7122 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7124 mod_chanmode_format(&channel
->modes
, buf
);
7125 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7128 high_present
= chanserv_write_users(ctx
, channel
->users
);
7129 chanserv_write_bans(ctx
, channel
->bans
);
7131 if(dict_size(channel
->notes
))
7135 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7136 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7138 struct note
*note
= iter_data(it
);
7139 saxdb_start_record(ctx
, iter_key(it
), 0);
7140 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7141 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7142 saxdb_end_record(ctx
);
7144 saxdb_end_record(ctx
);
7147 if(channel
->ownerTransfer
)
7148 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7149 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7150 saxdb_end_record(ctx
);
7154 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7158 saxdb_start_record(ctx
, ntype
->name
, 0);
7159 switch(ntype
->set_access_type
)
7161 case NOTE_SET_CHANNEL_ACCESS
:
7162 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7164 case NOTE_SET_CHANNEL_SETTER
:
7165 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7167 case NOTE_SET_PRIVILEGED
: default:
7168 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7171 switch(ntype
->visible_type
)
7173 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7174 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7175 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7177 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7178 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7179 saxdb_end_record(ctx
);
7183 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7185 struct do_not_register
*dnr
;
7188 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7190 dnr
= iter_data(it
);
7191 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7193 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7194 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7195 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7196 saxdb_end_record(ctx
);
7201 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7204 struct chanData
*channel
;
7207 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7208 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7209 chanserv_write_note_type(ctx
, iter_data(it
));
7210 saxdb_end_record(ctx
);
7213 saxdb_start_record(ctx
, KEY_DNR
, 1);
7214 write_dnrs_helper(ctx
, handle_dnrs
);
7215 write_dnrs_helper(ctx
, plain_dnrs
);
7216 write_dnrs_helper(ctx
, mask_dnrs
);
7217 saxdb_end_record(ctx
);
7220 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7221 for(channel
= channelList
; channel
; channel
= channel
->next
)
7222 chanserv_write_channel(ctx
, channel
);
7223 saxdb_end_record(ctx
);
7229 chanserv_db_cleanup(void) {
7231 unreg_part_func(handle_part
);
7233 unregister_channel(channelList
, "terminating.");
7234 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7235 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7236 free(chanserv_conf
.support_channels
.list
);
7237 dict_delete(handle_dnrs
);
7238 dict_delete(plain_dnrs
);
7239 dict_delete(mask_dnrs
);
7240 dict_delete(note_types
);
7241 free_string_list(chanserv_conf
.eightball
);
7242 free_string_list(chanserv_conf
.old_ban_names
);
7243 free_string_list(chanserv_conf
.set_shows
);
7244 free(set_shows_list
.list
);
7245 free(uset_shows_list
.list
);
7248 struct userData
*helper
= helperList
;
7249 helperList
= helperList
->next
;
7254 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7255 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7256 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7259 init_chanserv(const char *nick
)
7261 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7262 conf_register_reload(chanserv_conf_read
);
7264 reg_server_link_func(handle_server_link
);
7266 reg_new_channel_func(handle_new_channel
);
7267 reg_join_func(handle_join
);
7268 reg_part_func(handle_part
);
7269 reg_kick_func(handle_kick
);
7270 reg_topic_func(handle_topic
);
7271 reg_mode_change_func(handle_mode
);
7272 reg_nick_change_func(handle_nick_change
);
7274 reg_auth_func(handle_auth
);
7275 reg_handle_rename_func(handle_rename
);
7276 reg_unreg_func(handle_unreg
);
7278 handle_dnrs
= dict_new();
7279 dict_set_free_data(handle_dnrs
, free
);
7280 plain_dnrs
= dict_new();
7281 dict_set_free_data(plain_dnrs
, free
);
7282 mask_dnrs
= dict_new();
7283 dict_set_free_data(mask_dnrs
, free
);
7285 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7286 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7287 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7288 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7289 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7290 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7291 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7292 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7293 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7294 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7296 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7297 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7299 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7300 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7301 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7302 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7303 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7305 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7306 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7307 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7308 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7309 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7310 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7312 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7313 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7314 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7315 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7317 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7318 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7319 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7320 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7321 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7322 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7323 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7324 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7325 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7326 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7328 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7329 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7330 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7331 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7332 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7333 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7334 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7335 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7336 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7337 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7338 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7339 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7340 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7341 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7343 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7344 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7345 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7346 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7347 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7349 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7350 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7352 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7353 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7354 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7355 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7356 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7357 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7358 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7359 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7360 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7361 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7362 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7363 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7365 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7366 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7368 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7369 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7370 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7371 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7373 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7374 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7375 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7376 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7377 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7379 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7380 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7381 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7382 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7383 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7384 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7385 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7387 /* Channel options */
7388 DEFINE_CHANNEL_OPTION(defaulttopic
);
7389 DEFINE_CHANNEL_OPTION(topicmask
);
7390 DEFINE_CHANNEL_OPTION(greeting
);
7391 DEFINE_CHANNEL_OPTION(usergreeting
);
7392 DEFINE_CHANNEL_OPTION(modes
);
7393 DEFINE_CHANNEL_OPTION(enfops
);
7394 DEFINE_CHANNEL_OPTION(enfhalfops
);
7395 DEFINE_CHANNEL_OPTION(giveops
);
7396 DEFINE_CHANNEL_OPTION(givehalfops
);
7397 DEFINE_CHANNEL_OPTION(protect
);
7398 DEFINE_CHANNEL_OPTION(enfmodes
);
7399 DEFINE_CHANNEL_OPTION(enftopic
);
7400 DEFINE_CHANNEL_OPTION(pubcmd
);
7401 DEFINE_CHANNEL_OPTION(givevoice
);
7402 DEFINE_CHANNEL_OPTION(userinfo
);
7403 DEFINE_CHANNEL_OPTION(dynlimit
);
7404 DEFINE_CHANNEL_OPTION(topicsnarf
);
7405 DEFINE_CHANNEL_OPTION(nodelete
);
7406 DEFINE_CHANNEL_OPTION(toys
);
7407 DEFINE_CHANNEL_OPTION(setters
);
7408 DEFINE_CHANNEL_OPTION(topicrefresh
);
7409 DEFINE_CHANNEL_OPTION(ctcpusers
);
7410 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7411 DEFINE_CHANNEL_OPTION(inviteme
);
7413 DEFINE_CHANNEL_OPTION(offchannel
);
7414 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7416 /* Alias set topic to set defaulttopic for compatibility. */
7417 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7420 DEFINE_USER_OPTION(noautoop
);
7421 DEFINE_USER_OPTION(autoinvite
);
7422 DEFINE_USER_OPTION(info
);
7424 /* Alias uset autovoice to uset autoop. */
7425 modcmd_register(chanserv_module
, "uset noautovoice", user_opt_noautoop
, 1, 0, NULL
);
7427 note_types
= dict_new();
7428 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7431 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7432 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7433 service_register(chanserv
)->trigger
= '!';
7434 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7436 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7438 if(chanserv_conf
.channel_expire_frequency
)
7439 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7441 if(chanserv_conf
.refresh_period
)
7443 time_t next_refresh
;
7444 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7445 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7448 reg_exit_func(chanserv_db_cleanup
);
7449 message_register_table(msgtab
);