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 struct string_list
*set_shows
;
513 struct string_list
*eightball
;
514 struct string_list
*old_ban_names
;
516 const char *ctcp_short_ban_duration
;
517 const char *ctcp_long_ban_duration
;
519 const char *irc_operator_epithet
;
520 const char *network_helper_epithet
;
521 const char *support_helper_epithet
;
526 struct userNode
*user
;
527 struct userNode
*bot
;
528 struct chanNode
*channel
;
530 unsigned short lowest
;
531 unsigned short highest
;
532 struct userData
**users
;
533 struct helpfile_table table
;
536 enum note_access_type
538 NOTE_SET_CHANNEL_ACCESS
,
539 NOTE_SET_CHANNEL_SETTER
,
543 enum note_visible_type
546 NOTE_VIS_CHANNEL_USERS
,
552 enum note_access_type set_access_type
;
554 unsigned int min_opserv
;
555 unsigned short min_ulevel
;
557 enum note_visible_type visible_type
;
558 unsigned int max_length
;
565 struct note_type
*type
;
566 char setter
[NICKSERV_HANDLE_LEN
+1];
570 static unsigned int registered_channels
;
571 static unsigned int banCount
;
573 static const struct {
576 unsigned short level
;
579 { "peon", "Peon", UL_PEON
, '+' },
580 { "halfop", "HalfOp", UL_HALFOP
, '%' },
581 { "op", "Op", UL_OP
, '@' },
582 { "manager", "Manager", UL_MANAGER
, '%' },
583 { "coowner", "Coowner", UL_COOWNER
, '*' },
584 { "owner", "Owner", UL_OWNER
, '!' },
585 { "helper", "BUG:", UL_HELPER
, 'X' }
588 static const struct {
591 unsigned short default_value
;
592 unsigned int old_idx
;
593 unsigned int old_flag
;
594 unsigned short flag_value
;
596 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
597 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
598 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
599 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
600 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
601 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
602 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
603 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
604 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
605 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
606 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
607 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
608 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
611 struct charOptionValues
{
614 } protectValues
[] = {
615 { 'a', "CSMSG_PROTECT_ALL" },
616 { 'e', "CSMSG_PROTECT_EQUAL" },
617 { 'l', "CSMSG_PROTECT_LOWER" },
618 { 'n', "CSMSG_PROTECT_NONE" }
620 { 'd', "CSMSG_TOYS_DISABLED" },
621 { 'n', "CSMSG_TOYS_PRIVATE" },
622 { 'p', "CSMSG_TOYS_PUBLIC" }
623 }, topicRefreshValues
[] = {
624 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
625 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
626 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
627 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
628 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
629 }, ctcpReactionValues
[] = {
630 { 'k', "CSMSG_CTCPREACTION_KICK" },
631 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
632 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
633 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
636 static const struct {
640 unsigned int old_idx
;
642 struct charOptionValues
*values
;
644 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
645 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
646 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
647 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
650 struct userData
*helperList
;
651 struct chanData
*channelList
;
652 static struct module *chanserv_module
;
653 static unsigned int userCount
;
655 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
656 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
657 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
660 user_level_from_name(const char *name
, unsigned short clamp_level
)
662 unsigned int level
= 0, ii
;
664 level
= strtoul(name
, NULL
, 10);
665 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
666 if(!irccasecmp(name
, accessLevels
[ii
].name
))
667 level
= accessLevels
[ii
].level
;
668 if(level
> clamp_level
)
674 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
677 *minl
= strtoul(arg
, &sep
, 10);
685 *maxl
= strtoul(sep
+1, &sep
, 10);
693 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
695 struct userData
*uData
, **head
;
697 if(!channel
|| !handle
)
700 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
701 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
703 for(uData
= helperList
;
704 uData
&& uData
->handle
!= handle
;
705 uData
= uData
->next
);
709 uData
= calloc(1, sizeof(struct userData
));
710 uData
->handle
= handle
;
712 uData
->access
= UL_HELPER
;
718 uData
->next
= helperList
;
720 helperList
->prev
= uData
;
728 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
729 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
732 head
= &(channel
->users
);
735 if(uData
&& (uData
!= *head
))
737 /* Shuffle the user to the head of whatever list he was in. */
739 uData
->next
->prev
= uData
->prev
;
741 uData
->prev
->next
= uData
->next
;
747 (**head
).prev
= uData
;
754 /* Returns non-zero if user has at least the minimum access.
755 * exempt_owner is set when handling !set, so the owner can set things
758 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
760 struct userData
*uData
;
761 struct chanData
*cData
= channel
->channel_info
;
762 unsigned short minimum
= cData
->lvlOpts
[opt
];
765 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
768 if(minimum
<= uData
->access
)
770 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
775 /* Scan for other users authenticated to the same handle
776 still in the channel. If so, keep them listed as present.
778 user is optional, if not null, it skips checking that userNode
779 (for the handle_part function) */
781 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
785 if(IsSuspended(uData
->channel
)
786 || IsUserSuspended(uData
)
787 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
799 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
801 unsigned int eflags
, argc
;
803 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
805 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
806 if(!channel
->channel_info
807 || IsSuspended(channel
->channel_info
)
809 || !ircncasecmp(text
, "ACTION ", 7))
811 /* Figure out the minimum level needed to CTCP the channel */
812 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
814 /* We need to enforce against them; do so. */
817 argv
[1] = user
->nick
;
819 if(GetUserMode(channel
, user
))
820 eflags
|= ACTION_KICK
;
821 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
822 default: case 'k': /* just do the kick */ break;
824 eflags
|= ACTION_BAN
;
827 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
828 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
831 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
832 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
835 argv
[argc
++] = bad_ctcp_reason
;
836 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
840 chanserv_create_note_type(const char *name
)
842 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
843 strcpy(ntype
->name
, name
);
845 dict_insert(note_types
, ntype
->name
, ntype
);
850 chanserv_deref_note_type(void *data
)
852 struct note_type
*ntype
= data
;
854 if(--ntype
->refs
> 0)
860 chanserv_flush_note_type(struct note_type
*ntype
)
862 struct chanData
*cData
;
863 for(cData
= channelList
; cData
; cData
= cData
->next
)
864 dict_remove(cData
->notes
, ntype
->name
);
868 chanserv_truncate_notes(struct note_type
*ntype
)
870 struct chanData
*cData
;
872 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
874 for(cData
= channelList
; cData
; cData
= cData
->next
) {
875 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
878 if(strlen(note
->note
) <= ntype
->max_length
)
880 dict_remove2(cData
->notes
, ntype
->name
, 1);
881 note
= realloc(note
, size
);
882 note
->note
[ntype
->max_length
] = 0;
883 dict_insert(cData
->notes
, ntype
->name
, note
);
887 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
890 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
893 unsigned int len
= strlen(text
);
895 if(len
> type
->max_length
) len
= type
->max_length
;
896 note
= calloc(1, sizeof(*note
) + len
);
898 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
899 memcpy(note
->note
, text
, len
);
901 dict_insert(channel
->notes
, type
->name
, note
);
907 chanserv_free_note(void *data
)
909 struct note
*note
= data
;
911 chanserv_deref_note_type(note
->type
);
912 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
916 static MODCMD_FUNC(cmd_createnote
) {
917 struct note_type
*ntype
;
918 unsigned int arg
= 1, existed
= 0, max_length
;
920 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
923 ntype
= chanserv_create_note_type(argv
[arg
]);
924 if(!irccasecmp(argv
[++arg
], "privileged"))
927 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
928 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
930 else if(!irccasecmp(argv
[arg
], "channel"))
932 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
935 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
938 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
939 ntype
->set_access
.min_ulevel
= ulvl
;
941 else if(!irccasecmp(argv
[arg
], "setter"))
943 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
947 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
951 if(!irccasecmp(argv
[++arg
], "privileged"))
952 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
953 else if(!irccasecmp(argv
[arg
], "channel_users"))
954 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
955 else if(!irccasecmp(argv
[arg
], "all"))
956 ntype
->visible_type
= NOTE_VIS_ALL
;
958 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
962 if((arg
+1) >= argc
) {
963 reply("MSG_MISSING_PARAMS", argv
[0]);
966 max_length
= strtoul(argv
[++arg
], NULL
, 0);
967 if(max_length
< 20 || max_length
> 450)
969 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
972 if(existed
&& (max_length
< ntype
->max_length
))
974 ntype
->max_length
= max_length
;
975 chanserv_truncate_notes(ntype
);
977 ntype
->max_length
= max_length
;
980 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
982 reply("CSMSG_NOTE_CREATED", ntype
->name
);
987 dict_remove(note_types
, ntype
->name
);
991 static MODCMD_FUNC(cmd_removenote
) {
992 struct note_type
*ntype
;
995 ntype
= dict_find(note_types
, argv
[1], NULL
);
996 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
999 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1006 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1009 chanserv_flush_note_type(ntype
);
1011 dict_remove(note_types
, argv
[1]);
1012 reply("CSMSG_NOTE_DELETED", argv
[1]);
1017 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1021 if(orig
->modes_set
& change
->modes_clear
)
1023 if(orig
->modes_clear
& change
->modes_set
)
1025 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1026 && strcmp(orig
->new_key
, change
->new_key
))
1028 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1029 && (orig
->new_limit
!= change
->new_limit
))
1034 static char max_length_text
[MAXLEN
+1][16];
1036 static struct helpfile_expansion
1037 chanserv_expand_variable(const char *variable
)
1039 struct helpfile_expansion exp
;
1041 if(!irccasecmp(variable
, "notes"))
1044 exp
.type
= HF_TABLE
;
1045 exp
.value
.table
.length
= 1;
1046 exp
.value
.table
.width
= 3;
1047 exp
.value
.table
.flags
= 0;
1048 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1049 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1050 exp
.value
.table
.contents
[0][0] = "Note Type";
1051 exp
.value
.table
.contents
[0][1] = "Visibility";
1052 exp
.value
.table
.contents
[0][2] = "Max Length";
1053 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1055 struct note_type
*ntype
= iter_data(it
);
1058 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1059 row
= exp
.value
.table
.length
++;
1060 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1061 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1062 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1063 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1065 if(!max_length_text
[ntype
->max_length
][0])
1066 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1067 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1072 exp
.type
= HF_STRING
;
1073 exp
.value
.str
= NULL
;
1077 static struct chanData
*
1078 register_channel(struct chanNode
*cNode
, char *registrar
)
1080 struct chanData
*channel
;
1081 enum levelOption lvlOpt
;
1082 enum charOption chOpt
;
1084 channel
= calloc(1, sizeof(struct chanData
));
1086 channel
->notes
= dict_new();
1087 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1089 channel
->registrar
= strdup(registrar
);
1090 channel
->registered
= now
;
1091 channel
->visited
= now
;
1092 channel
->limitAdjusted
= now
;
1093 channel
->ownerTransfer
= now
;
1094 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1095 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1096 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1097 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1098 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1100 channel
->prev
= NULL
;
1101 channel
->next
= channelList
;
1104 channelList
->prev
= channel
;
1105 channelList
= channel
;
1106 registered_channels
++;
1108 channel
->channel
= cNode
;
1110 cNode
->channel_info
= channel
;
1115 static struct userData
*
1116 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1118 struct userData
*ud
;
1120 if(access
> UL_OWNER
)
1123 ud
= calloc(1, sizeof(*ud
));
1124 ud
->channel
= channel
;
1125 ud
->handle
= handle
;
1127 ud
->access
= access
;
1128 ud
->info
= info
? strdup(info
) : NULL
;
1131 ud
->next
= channel
->users
;
1133 channel
->users
->prev
= ud
;
1134 channel
->users
= ud
;
1136 channel
->userCount
++;
1140 ud
->u_next
= ud
->handle
->channels
;
1142 ud
->u_next
->u_prev
= ud
;
1143 ud
->handle
->channels
= ud
;
1148 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1151 del_channel_user(struct userData
*user
, int do_gc
)
1153 struct chanData
*channel
= user
->channel
;
1155 channel
->userCount
--;
1159 user
->prev
->next
= user
->next
;
1161 channel
->users
= user
->next
;
1163 user
->next
->prev
= user
->prev
;
1166 user
->u_prev
->u_next
= user
->u_next
;
1168 user
->handle
->channels
= user
->u_next
;
1170 user
->u_next
->u_prev
= user
->u_prev
;
1174 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1175 unregister_channel(channel
, "lost all users.");
1178 static void expire_ban(void *data
);
1180 static struct banData
*
1181 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1184 unsigned int ii
, l1
, l2
;
1189 bd
= malloc(sizeof(struct banData
));
1191 bd
->channel
= channel
;
1193 bd
->triggered
= triggered
;
1194 bd
->expires
= expires
;
1196 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1198 extern const char *hidden_host_suffix
;
1199 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1203 l2
= strlen(old_name
);
1206 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1208 new_mask
= alloca(MAXLEN
);
1209 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1212 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1214 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1215 bd
->reason
= reason
? strdup(reason
) : NULL
;
1218 timeq_add(expires
, expire_ban
, bd
);
1221 bd
->next
= channel
->bans
;
1223 channel
->bans
->prev
= bd
;
1225 channel
->banCount
++;
1232 del_channel_ban(struct banData
*ban
)
1234 ban
->channel
->banCount
--;
1238 ban
->prev
->next
= ban
->next
;
1240 ban
->channel
->bans
= ban
->next
;
1243 ban
->next
->prev
= ban
->prev
;
1246 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1255 expire_ban(void *data
)
1257 struct banData
*bd
= data
;
1258 if(!IsSuspended(bd
->channel
))
1260 struct banList bans
;
1261 struct mod_chanmode change
;
1263 bans
= bd
->channel
->channel
->banlist
;
1264 mod_chanmode_init(&change
);
1265 for(ii
=0; ii
<bans
.used
; ii
++)
1267 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1270 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1271 change
.args
[0].u
.hostmask
= bd
->mask
;
1272 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1278 del_channel_ban(bd
);
1281 static void chanserv_expire_suspension(void *data
);
1284 unregister_channel(struct chanData
*channel
, const char *reason
)
1286 struct mod_chanmode change
;
1287 char msgbuf
[MAXLEN
];
1289 /* After channel unregistration, the following must be cleaned
1291 - Channel information.
1294 - Channel suspension data.
1295 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1301 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1305 mod_chanmode_init(&change
);
1306 change
.modes_clear
|= MODE_REGISTERED
;
1307 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1310 while(channel
->users
)
1311 del_channel_user(channel
->users
, 0);
1313 while(channel
->bans
)
1314 del_channel_ban(channel
->bans
);
1316 free(channel
->topic
);
1317 free(channel
->registrar
);
1318 free(channel
->greeting
);
1319 free(channel
->user_greeting
);
1320 free(channel
->topic_mask
);
1323 channel
->prev
->next
= channel
->next
;
1325 channelList
= channel
->next
;
1328 channel
->next
->prev
= channel
->prev
;
1330 if(channel
->suspended
)
1332 struct chanNode
*cNode
= channel
->channel
;
1333 struct suspended
*suspended
, *next_suspended
;
1335 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1337 next_suspended
= suspended
->previous
;
1338 free(suspended
->suspender
);
1339 free(suspended
->reason
);
1340 if(suspended
->expires
)
1341 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1346 cNode
->channel_info
= NULL
;
1348 channel
->channel
->channel_info
= NULL
;
1350 dict_delete(channel
->notes
);
1351 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1352 if(!IsSuspended(channel
))
1353 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1354 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1355 UnlockChannel(channel
->channel
);
1357 registered_channels
--;
1361 expire_channels(UNUSED_ARG(void *data
))
1363 struct chanData
*channel
, *next
;
1364 struct userData
*user
;
1365 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1367 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1368 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1370 for(channel
= channelList
; channel
; channel
= next
)
1372 next
= channel
->next
;
1374 /* See if the channel can be expired. */
1375 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1376 || IsProtected(channel
))
1379 /* Make sure there are no high-ranking users still in the channel. */
1380 for(user
=channel
->users
; user
; user
=user
->next
)
1381 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1386 /* Unregister the channel */
1387 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1388 unregister_channel(channel
, "registration expired.");
1391 if(chanserv_conf
.channel_expire_frequency
)
1392 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1396 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1398 char protect
= channel
->chOpts
[chProtect
];
1399 struct userData
*cs_victim
, *cs_aggressor
;
1401 /* Don't protect if no one is to be protected, someone is attacking
1402 himself, or if the aggressor is an IRC Operator. */
1403 if(protect
== 'n' || victim
== aggressor
|| IsOper(aggressor
))
1406 /* Don't protect if the victim isn't authenticated (because they
1407 can't be a channel user), unless we are to protect non-users
1409 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1410 if(protect
!= 'a' && !cs_victim
)
1413 /* Protect if the aggressor isn't a user because at this point,
1414 the aggressor can only be less than or equal to the victim. */
1415 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1419 /* If the aggressor was a user, then the victim can't be helped. */
1426 if(cs_victim
->access
> cs_aggressor
->access
)
1431 if(cs_victim
->access
>= cs_aggressor
->access
)
1440 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1442 struct chanData
*cData
= channel
->channel_info
;
1443 struct userData
*cs_victim
;
1445 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1446 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveOps
]))
1447 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1449 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1457 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1459 struct chanData
*cData
= channel
->channel_info
;
1460 struct userData
*cs_victim
;
1462 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1463 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveHalfOps
]))
1464 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1466 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1475 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1477 if(IsService(victim
))
1479 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1483 if(protect_user(victim
, user
, channel
->channel_info
))
1485 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1493 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1495 if(IsService(victim
))
1497 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1501 if(protect_user(victim
, user
, channel
->channel_info
))
1503 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1510 static struct do_not_register
*
1511 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1513 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1514 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1515 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1516 strcpy(dnr
->reason
, reason
);
1518 if(dnr
->chan_name
[0] == '*')
1519 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1520 else if(strpbrk(dnr
->chan_name
, "*?"))
1521 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1523 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1527 static struct dnrList
1528 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1530 struct dnrList list
;
1532 struct do_not_register
*dnr
;
1534 dnrList_init(&list
);
1535 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1536 dnrList_append(&list
, dnr
);
1537 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1538 dnrList_append(&list
, dnr
);
1540 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1541 if(match_ircglob(chan_name
, iter_key(it
)))
1542 dnrList_append(&list
, iter_data(it
));
1547 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1549 struct dnrList list
;
1550 struct do_not_register
*dnr
;
1552 char buf
[INTERVALLEN
];
1554 list
= chanserv_find_dnrs(chan_name
, handle
);
1555 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1557 dnr
= list
.list
[ii
];
1560 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1561 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1564 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1567 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1572 struct do_not_register
*
1573 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1575 struct do_not_register
*dnr
;
1578 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1582 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1584 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1585 if(match_ircglob(chan_name
, iter_key(it
)))
1586 return iter_data(it
);
1591 static CHANSERV_FUNC(cmd_noregister
)
1594 struct do_not_register
*dnr
;
1595 char buf
[INTERVALLEN
];
1596 unsigned int matches
;
1602 reply("CSMSG_DNR_SEARCH_RESULTS");
1604 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1606 dnr
= iter_data(it
);
1608 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1610 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1613 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1615 dnr
= iter_data(it
);
1617 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1619 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1622 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1624 dnr
= iter_data(it
);
1626 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1628 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1633 reply("MSG_MATCH_COUNT", matches
);
1635 reply("MSG_NO_MATCHES");
1641 if(!IsChannelName(target
) && (*target
!= '*'))
1643 reply("CSMSG_NOT_DNR", target
);
1649 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1650 if((*target
== '*') && !get_handle_info(target
+ 1))
1652 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1655 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1656 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1660 reply("CSMSG_DNR_SEARCH_RESULTS");
1662 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1664 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1666 reply("MSG_NO_MATCHES");
1670 static CHANSERV_FUNC(cmd_allowregister
)
1672 const char *chan_name
= argv
[1];
1674 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1676 dict_remove(handle_dnrs
, chan_name
+1);
1677 reply("CSMSG_DNR_REMOVED", chan_name
);
1679 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1681 dict_remove(plain_dnrs
, chan_name
);
1682 reply("CSMSG_DNR_REMOVED", chan_name
);
1684 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1686 dict_remove(mask_dnrs
, chan_name
);
1687 reply("CSMSG_DNR_REMOVED", chan_name
);
1691 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1698 chanserv_get_owned_count(struct handle_info
*hi
)
1700 struct userData
*cList
;
1703 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1704 if(cList
->access
== UL_OWNER
)
1709 static CHANSERV_FUNC(cmd_register
)
1711 struct mod_chanmode
*change
;
1712 struct handle_info
*handle
;
1713 struct chanData
*cData
;
1714 struct modeNode
*mn
;
1715 char reason
[MAXLEN
];
1717 unsigned int new_channel
, force
=0;
1718 struct do_not_register
*dnr
;
1722 if(channel
->channel_info
)
1724 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1728 if(channel
->bad_channel
)
1730 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1734 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1736 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1741 chan_name
= channel
->name
;
1745 if((argc
< 2) || !IsChannelName(argv
[1]))
1747 reply("MSG_NOT_CHANNEL_NAME");
1751 if(opserv_bad_channel(argv
[1]))
1753 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1758 chan_name
= argv
[1];
1761 if(argc
>= (new_channel
+2))
1763 if(!IsHelping(user
))
1765 reply("CSMSG_PROXY_FORBIDDEN");
1769 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1771 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1772 dnr
= chanserv_is_dnr(chan_name
, handle
);
1776 handle
= user
->handle_info
;
1777 dnr
= chanserv_is_dnr(chan_name
, handle
);
1781 if(!IsHelping(user
))
1782 reply("CSMSG_DNR_CHANNEL", chan_name
);
1784 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1788 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1790 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1795 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1797 cData
= register_channel(channel
, user
->handle_info
->handle
);
1798 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1799 cData
->modes
= chanserv_conf
.default_modes
;
1801 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1802 change
= mod_chanmode_dup(&cData
->modes
, 1);
1803 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1804 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1806 mod_chanmode_announce(chanserv
, channel
, change
);
1807 mod_chanmode_free(change
);
1809 /* Initialize the channel's max user record. */
1810 cData
->max
= channel
->members
.used
;
1812 if(handle
!= user
->handle_info
)
1813 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1815 reply("CSMSG_REG_SUCCESS", channel
->name
);
1817 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1818 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1823 make_confirmation_string(struct userData
*uData
)
1825 static char strbuf
[16];
1830 for(src
= uData
->handle
->handle
; *src
; )
1831 accum
= accum
* 31 + toupper(*src
++);
1833 for(src
= uData
->channel
->channel
->name
; *src
; )
1834 accum
= accum
* 31 + toupper(*src
++);
1835 sprintf(strbuf
, "%08x", accum
);
1839 static CHANSERV_FUNC(cmd_unregister
)
1842 char reason
[MAXLEN
];
1843 struct chanData
*cData
;
1844 struct userData
*uData
;
1846 cData
= channel
->channel_info
;
1849 reply("CSMSG_NOT_REGISTERED", channel
->name
);
1853 uData
= GetChannelUser(cData
, user
->handle_info
);
1854 if(!uData
|| (uData
->access
< UL_OWNER
))
1856 reply("CSMSG_NO_ACCESS");
1860 if(IsProtected(cData
))
1862 reply("CSMSG_UNREG_NODELETE", channel
->name
);
1866 if(!IsHelping(user
))
1868 const char *confirm_string
;
1869 if(IsSuspended(cData
))
1871 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
1874 confirm_string
= make_confirmation_string(uData
);
1875 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
1877 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
1882 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
1883 name
= strdup(channel
->name
);
1884 unregister_channel(cData
, reason
);
1885 reply("CSMSG_UNREG_SUCCESS", name
);
1890 static CHANSERV_FUNC(cmd_move
)
1892 struct mod_chanmode change
;
1893 struct chanNode
*target
;
1894 struct modeNode
*mn
;
1895 struct userData
*uData
;
1896 char reason
[MAXLEN
];
1897 struct do_not_register
*dnr
;
1901 if(IsProtected(channel
->channel_info
))
1903 reply("CSMSG_MOVE_NODELETE", channel
->name
);
1907 if(!IsChannelName(argv
[1]))
1909 reply("MSG_NOT_CHANNEL_NAME");
1913 if(opserv_bad_channel(argv
[1]))
1915 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1919 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
1921 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
1923 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
1925 if(!IsHelping(user
))
1926 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
1928 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
1934 mod_chanmode_init(&change
);
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
))
1955 change
.args
[0].mode
= MODE_CHANOP
;
1956 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
1957 mod_chanmode_announce(chanserv
, target
, &change
);
1962 /* Clear MODE_REGISTERED from old channel, add it to new. */
1964 change
.modes_clear
= MODE_REGISTERED
;
1965 mod_chanmode_announce(chanserv
, channel
, &change
);
1966 change
.modes_clear
= 0;
1967 change
.modes_set
= MODE_REGISTERED
;
1968 mod_chanmode_announce(chanserv
, target
, &change
);
1971 /* Move the channel_info to the target channel; it
1972 shouldn't be necessary to clear timeq callbacks
1973 for the old channel. */
1974 target
->channel_info
= channel
->channel_info
;
1975 target
->channel_info
->channel
= target
;
1976 channel
->channel_info
= NULL
;
1978 reply("CSMSG_MOVE_SUCCESS", target
->name
);
1980 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
1981 if(!IsSuspended(target
->channel_info
))
1983 char reason2
[MAXLEN
];
1984 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
1985 DelChannelUser(chanserv
, channel
, reason2
, 0);
1987 UnlockChannel(channel
);
1988 LockChannel(target
);
1989 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1994 merge_users(struct chanData
*source
, struct chanData
*target
)
1996 struct userData
*suData
, *tuData
, *next
;
2002 /* Insert the source's users into the scratch area. */
2003 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2004 dict_insert(merge
, suData
->handle
->handle
, suData
);
2006 /* Iterate through the target's users, looking for
2007 users common to both channels. The lower access is
2008 removed from either the scratch area or target user
2010 for(tuData
= target
->users
; tuData
; tuData
= next
)
2012 struct userData
*choice
;
2014 next
= tuData
->next
;
2016 /* If a source user exists with the same handle as a target
2017 channel's user, resolve the conflict by removing one. */
2018 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2022 /* Pick the data we want to keep. */
2023 /* If the access is the same, use the later seen time. */
2024 if(suData
->access
== tuData
->access
)
2025 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2026 else /* Otherwise, keep the higher access level. */
2027 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2029 /* Remove the user that wasn't picked. */
2030 if(choice
== tuData
)
2032 dict_remove(merge
, suData
->handle
->handle
);
2033 del_channel_user(suData
, 0);
2036 del_channel_user(tuData
, 0);
2039 /* Move the remaining users to the target channel. */
2040 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2042 suData
= iter_data(it
);
2044 /* Insert the user into the target channel's linked list. */
2045 suData
->prev
= NULL
;
2046 suData
->next
= target
->users
;
2047 suData
->channel
= target
;
2050 target
->users
->prev
= suData
;
2051 target
->users
= suData
;
2053 /* Update the user counts for the target channel; the
2054 source counts are left alone. */
2055 target
->userCount
++;
2058 /* Possible to assert (source->users == NULL) here. */
2059 source
->users
= NULL
;
2064 merge_bans(struct chanData
*source
, struct chanData
*target
)
2066 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2068 /* Hold on to the original head of the target ban list
2069 to avoid comparing source bans with source bans. */
2070 tFront
= target
->bans
;
2072 /* Perform a totally expensive O(n*m) merge, ick. */
2073 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2075 /* Flag to track whether the ban's been moved
2076 to the destination yet. */
2079 /* Possible to assert (sbData->prev == NULL) here. */
2080 sNext
= sbData
->next
;
2082 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2084 tNext
= tbData
->next
;
2086 /* Perform two comparisons between each source
2087 and target ban, conflicts are resolved by
2088 keeping the broader ban and copying the later
2089 expiration and triggered time. */
2090 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2092 /* There is a broader ban in the target channel that
2093 overrides one in the source channel; remove the
2094 source ban and break. */
2095 if(sbData
->expires
> tbData
->expires
)
2096 tbData
->expires
= sbData
->expires
;
2097 if(sbData
->triggered
> tbData
->triggered
)
2098 tbData
->triggered
= sbData
->triggered
;
2099 del_channel_ban(sbData
);
2102 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2104 /* There is a broader ban in the source channel that
2105 overrides one in the target channel; remove the
2106 target ban, fall through and move the source over. */
2107 if(tbData
->expires
> sbData
->expires
)
2108 sbData
->expires
= tbData
->expires
;
2109 if(tbData
->triggered
> sbData
->triggered
)
2110 sbData
->triggered
= tbData
->triggered
;
2111 if(tbData
== tFront
)
2113 del_channel_ban(tbData
);
2116 /* Source bans can override multiple target bans, so
2117 we allow a source to run through this loop multiple
2118 times, but we can only move it once. */
2123 /* Remove the source ban from the source ban list. */
2125 sbData
->next
->prev
= sbData
->prev
;
2127 /* Modify the source ban's associated channel. */
2128 sbData
->channel
= target
;
2130 /* Insert the ban into the target channel's linked list. */
2131 sbData
->prev
= NULL
;
2132 sbData
->next
= target
->bans
;
2135 target
->bans
->prev
= sbData
;
2136 target
->bans
= sbData
;
2138 /* Update the user counts for the target channel. */
2143 /* Possible to assert (source->bans == NULL) here. */
2144 source
->bans
= NULL
;
2148 merge_data(struct chanData
*source
, struct chanData
*target
)
2150 if(source
->visited
> target
->visited
)
2151 target
->visited
= source
->visited
;
2155 merge_channel(struct chanData
*source
, struct chanData
*target
)
2157 merge_users(source
, target
);
2158 merge_bans(source
, target
);
2159 merge_data(source
, target
);
2162 static CHANSERV_FUNC(cmd_merge
)
2164 struct userData
*target_user
;
2165 struct chanNode
*target
;
2166 char reason
[MAXLEN
];
2170 /* Make sure the target channel exists and is registered to the user
2171 performing the command. */
2172 if(!(target
= GetChannel(argv
[1])))
2174 reply("MSG_INVALID_CHANNEL");
2178 if(!target
->channel_info
)
2180 reply("CSMSG_NOT_REGISTERED", target
->name
);
2184 if(IsProtected(channel
->channel_info
))
2186 reply("CSMSG_MERGE_NODELETE");
2190 if(IsSuspended(target
->channel_info
))
2192 reply("CSMSG_MERGE_SUSPENDED");
2196 if(channel
== target
)
2198 reply("CSMSG_MERGE_SELF");
2202 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2203 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2205 reply("CSMSG_MERGE_NOT_OWNER");
2209 /* Merge the channel structures and associated data. */
2210 merge_channel(channel
->channel_info
, target
->channel_info
);
2211 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2212 unregister_channel(channel
->channel_info
, reason
);
2213 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2217 static CHANSERV_FUNC(cmd_opchan
)
2219 struct mod_chanmode change
;
2220 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2222 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2225 channel
->channel_info
->may_opchan
= 0;
2226 mod_chanmode_init(&change
);
2228 change
.args
[0].mode
= MODE_CHANOP
;
2229 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2230 mod_chanmode_announce(chanserv
, channel
, &change
);
2231 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2235 static CHANSERV_FUNC(cmd_adduser
)
2237 struct userData
*actee
;
2238 struct userData
*actor
;
2239 struct handle_info
*handle
;
2240 unsigned short access
;
2244 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2246 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2250 access
= user_level_from_name(argv
[2], UL_OWNER
);
2253 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2257 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2258 if(actor
->access
<= access
)
2260 reply("CSMSG_NO_BUMP_ACCESS");
2264 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2267 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2269 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, actee
->access
);
2273 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2274 scan_user_presence(actee
, NULL
);
2275 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, access
);
2279 static CHANSERV_FUNC(cmd_clvl
)
2281 struct handle_info
*handle
;
2282 struct userData
*victim
;
2283 struct userData
*actor
;
2284 unsigned short new_access
;
2285 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2289 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2291 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2294 if(handle
== user
->handle_info
&& !privileged
)
2296 reply("CSMSG_NO_SELF_CLVL");
2300 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2302 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2306 if(actor
->access
<= victim
->access
&& !privileged
)
2308 reply("MSG_USER_OUTRANKED", handle
->handle
);
2312 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2316 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2320 if(new_access
>= actor
->access
&& !privileged
)
2322 reply("CSMSG_NO_BUMP_ACCESS");
2326 victim
->access
= new_access
;
2327 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, new_access
, channel
->name
);
2331 static CHANSERV_FUNC(cmd_deluser
)
2333 struct handle_info
*handle
;
2334 struct userData
*victim
;
2335 struct userData
*actor
;
2336 unsigned short access
;
2341 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2343 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2346 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2348 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2354 access
= user_level_from_name(argv
[1], UL_OWNER
);
2357 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2360 if(access
!= victim
->access
)
2362 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, victim
->access
, argv
[1]);
2368 access
= victim
->access
;
2371 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2373 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2377 chan_name
= strdup(channel
->name
);
2378 del_channel_user(victim
, 1);
2379 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2385 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2387 struct userData
*actor
, *uData
, *next
;
2389 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2391 if(min_access
> max_access
)
2393 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2397 if((actor
->access
<= max_access
) && !IsHelping(user
))
2399 reply("CSMSG_NO_ACCESS");
2403 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2407 if((uData
->access
>= min_access
)
2408 && (uData
->access
<= max_access
)
2409 && match_ircglob(uData
->handle
->handle
, mask
))
2410 del_channel_user(uData
, 1);
2413 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2417 static CHANSERV_FUNC(cmd_mdelowner
)
2419 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2422 static CHANSERV_FUNC(cmd_mdelcoowner
)
2424 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2427 static CHANSERV_FUNC(cmd_mdelmanager
)
2429 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2432 static CHANSERV_FUNC(cmd_mdelop
)
2434 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2437 static CHANSERV_FUNC(cmd_mdelpeon
)
2439 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2442 static CHANSERV_FUNC(cmd_mdelhalfop
)
2444 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2449 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2451 struct banData
*bData
, *next
;
2452 char interval
[INTERVALLEN
];
2457 limit
= now
- duration
;
2458 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2462 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2465 del_channel_ban(bData
);
2469 intervalString(interval
, duration
, user
->handle_info
);
2470 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2475 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2477 struct userData
*actor
, *uData
, *next
;
2478 char interval
[INTERVALLEN
];
2482 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2483 if(min_access
> max_access
)
2485 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2489 if((actor
->access
<= max_access
) && !IsHelping(user
))
2491 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2496 limit
= now
- duration
;
2497 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2501 if((uData
->seen
> limit
) || uData
->present
)
2504 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2505 || (!max_access
&& (uData
->access
< actor
->access
)))
2507 del_channel_user(uData
, 1);
2515 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2517 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2521 static CHANSERV_FUNC(cmd_trim
)
2523 unsigned long duration
;
2524 unsigned short min_level
, max_level
;
2528 duration
= ParseInterval(argv
[2]);
2531 reply("CSMSG_CANNOT_TRIM");
2535 if(!irccasecmp(argv
[1], "bans"))
2537 cmd_trim_bans(user
, channel
, duration
);
2540 else if(!irccasecmp(argv
[1], "users"))
2542 cmd_trim_users(user
, channel
, 0, 0, duration
);
2545 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2547 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2550 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2552 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2557 reply("CSMSG_INVALID_TRIM", argv
[1]);
2562 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2563 to the user. cmd_all takes advantage of this. */
2564 static CHANSERV_FUNC(cmd_up
)
2566 struct mod_chanmode change
;
2567 struct userData
*uData
;
2570 mod_chanmode_init(&change
);
2572 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2573 if(!change
.args
[0].u
.member
)
2576 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2580 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2584 reply("CSMSG_GODMODE_UP", argv
[0]);
2587 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveOps
])
2589 change
.args
[0].mode
= MODE_CHANOP
;
2590 errmsg
= "CSMSG_ALREADY_OPPED";
2592 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
2594 change
.args
[0].mode
= MODE_HALFOP
;
2595 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2597 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveVoice
])
2599 change
.args
[0].mode
= MODE_VOICE
;
2600 errmsg
= "CSMSG_ALREADY_VOICED";
2605 reply("CSMSG_NO_ACCESS");
2608 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2609 if(!change
.args
[0].mode
)
2612 reply(errmsg
, channel
->name
);
2615 modcmd_chanmode_announce(&change
);
2619 static CHANSERV_FUNC(cmd_down
)
2621 struct mod_chanmode change
;
2623 mod_chanmode_init(&change
);
2625 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2626 if(!change
.args
[0].u
.member
)
2629 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2633 if(!change
.args
[0].u
.member
->modes
)
2636 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2640 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2641 modcmd_chanmode_announce(&change
);
2645 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
)
2647 struct userData
*cList
;
2649 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2651 if(IsSuspended(cList
->channel
)
2652 || IsUserSuspended(cList
)
2653 || !GetUserMode(cList
->channel
->channel
, user
))
2656 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2662 static CHANSERV_FUNC(cmd_upall
)
2664 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2667 static CHANSERV_FUNC(cmd_downall
)
2669 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2672 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2673 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2676 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
)
2678 unsigned int ii
, valid
;
2679 struct userNode
*victim
;
2680 struct mod_chanmode
*change
;
2682 change
= mod_chanmode_alloc(argc
- 1);
2684 for(ii
=valid
=0; ++ii
< argc
; )
2686 if(!(victim
= GetUserH(argv
[ii
])))
2688 change
->args
[valid
].mode
= mode
;
2689 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2690 if(!change
->args
[valid
].u
.member
)
2692 if(validate
&& !validate(user
, channel
, victim
))
2697 change
->argc
= valid
;
2698 if(valid
< (argc
-1))
2699 reply("CSMSG_PROCESS_FAILED");
2702 modcmd_chanmode_announce(change
);
2703 reply(action
, channel
->name
);
2705 mod_chanmode_free(change
);
2709 static CHANSERV_FUNC(cmd_op
)
2711 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2714 static CHANSERV_FUNC(cmd_hop
)
2716 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2719 static CHANSERV_FUNC(cmd_deop
)
2721 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2724 static CHANSERV_FUNC(cmd_dehop
)
2726 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2729 static CHANSERV_FUNC(cmd_voice
)
2731 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2734 static CHANSERV_FUNC(cmd_devoice
)
2736 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2740 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2746 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2748 struct modeNode
*mn
= channel
->members
.list
[ii
];
2750 if(IsService(mn
->user
))
2753 if(!user_matches_glob(mn
->user
, ban
, 1))
2756 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2760 victims
[(*victimCount
)++] = mn
;
2766 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2768 struct userNode
*victim
;
2769 struct modeNode
**victims
;
2770 unsigned int offset
, n
, victimCount
, duration
= 0;
2771 char *reason
= "Bye.", *ban
, *name
;
2772 char interval
[INTERVALLEN
];
2774 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2775 REQUIRE_PARAMS(offset
);
2778 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2779 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2781 /* Truncate the reason to a length of TOPICLEN, as
2782 the ircd does; however, leave room for an ellipsis
2783 and the kicker's nick. */
2784 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2788 if((victim
= GetUserH(argv
[1])))
2790 victims
= alloca(sizeof(victims
[0]));
2791 victims
[0] = GetUserMode(channel
, victim
);
2792 /* XXX: The comparison with ACTION_KICK is just because all
2793 * other actions can work on users outside the channel, and we
2794 * want to allow those (e.g. unbans) in that case. If we add
2795 * some other ejection action for in-channel users, change
2797 victimCount
= victims
[0] ? 1 : 0;
2799 if(IsService(victim
))
2801 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
2805 if((action
== ACTION_KICK
) && !victimCount
)
2807 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
2811 if(protect_user(victim
, user
, channel
->channel_info
))
2813 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2817 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
2818 name
= victim
->nick
;
2822 if(!is_ircmask(argv
[1]))
2824 reply("MSG_NICK_UNKNOWN", argv
[1]);
2828 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
2830 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
2832 reply("CSMSG_MASK_PROTECTED", argv
[1]);
2835 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
2837 victimCount
= 0; /* Dont deop etc ppl who match this */
2839 #ifdef entropy_lameness
2840 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
2842 reply("CSMSG_LAME_MASK", argv
[1]);
2847 if((action
== ACTION_KICK
) && (victimCount
== 0))
2849 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
2853 name
= ban
= strdup(argv
[1]);
2856 /* Truncate the ban in place if necessary; we must ensure
2857 that 'ban' is a valid ban mask before sanitizing it. */
2858 sanitize_ircmask(ban
);
2860 if(action
& ACTION_ADD_BAN
)
2862 struct banData
*bData
, *next
;
2864 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
2866 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
2871 if(action
& ACTION_ADD_TIMED_BAN
)
2873 duration
= ParseInterval(argv
[2]);
2877 reply("CSMSG_DURATION_TOO_LOW");
2881 else if(duration
> (86400 * 365 * 2))
2883 reply("CSMSG_DURATION_TOO_HIGH");
2889 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2891 if(match_ircglobs(bData
->mask
, ban
))
2893 int exact
= !irccasecmp(bData
->mask
, ban
);
2895 /* The ban is redundant; there is already a ban
2896 with the same effect in place. */
2900 free(bData
->reason
);
2901 bData
->reason
= strdup(reason
);
2902 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
2904 reply("CSMSG_REASON_CHANGE", ban
);
2908 if(exact
&& bData
->expires
)
2912 /* If the ban matches an existing one exactly,
2913 extend the expiration time if the provided
2914 duration is longer. */
2915 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
2917 bData
->expires
= now
+ duration
;
2928 /* Delete the expiration timeq entry and
2929 requeue if necessary. */
2930 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
2933 timeq_add(bData
->expires
, expire_ban
, bData
);
2937 /* automated kickban */
2940 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
2942 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2948 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2955 if(match_ircglobs(ban
, bData
->mask
))
2957 /* The ban we are adding makes previously existing
2958 bans redundant; silently remove them. */
2959 del_channel_ban(bData
);
2963 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
);
2965 name
= ban
= strdup(bData
->mask
);
2969 /* WHAT DOES THIS DO?? -Rubin */
2970 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
2972 extern const char *hidden_host_suffix
;
2973 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
2975 unsigned int l1
, l2
;
2978 l2
= strlen(old_name
);
2981 if(irccasecmp(ban
+ l1
- l2
, old_name
))
2983 new_mask
= malloc(MAXLEN
);
2984 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
2986 name
= ban
= new_mask
;
2991 if(action
& ACTION_BAN
)
2993 unsigned int exists
;
2994 struct mod_chanmode
*change
;
2996 if(channel
->banlist
.used
>= MAXBANS
)
2999 reply("CSMSG_BANLIST_FULL", channel
->name
);
3004 exists
= ChannelBanExists(channel
, ban
);
3005 change
= mod_chanmode_alloc(victimCount
+ 1);
3006 for(n
= 0; n
< victimCount
; ++n
)
3008 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3009 change
->args
[n
].u
.member
= victims
[n
];
3013 change
->args
[n
].mode
= MODE_BAN
;
3014 change
->args
[n
++].u
.hostmask
= ban
;
3018 modcmd_chanmode_announce(change
);
3020 mod_chanmode_announce(chanserv
, channel
, change
);
3021 mod_chanmode_free(change
);
3023 if(exists
&& (action
== ACTION_BAN
))
3026 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3032 if(action
& ACTION_KICK
)
3034 char kick_reason
[MAXLEN
];
3035 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3037 for(n
= 0; n
< victimCount
; n
++)
3038 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3043 /* No response, since it was automated. */
3045 else if(action
& ACTION_ADD_BAN
)
3048 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3050 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3052 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3053 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3054 else if(action
& ACTION_BAN
)
3055 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3056 else if(action
& ACTION_KICK
&& victimCount
)
3057 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3063 static CHANSERV_FUNC(cmd_kickban
)
3065 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3068 static CHANSERV_FUNC(cmd_kick
)
3070 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3073 static CHANSERV_FUNC(cmd_ban
)
3075 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3078 static CHANSERV_FUNC(cmd_addban
)
3080 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
3083 static CHANSERV_FUNC(cmd_addtimedban
)
3085 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
3088 static struct mod_chanmode
*
3089 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3091 struct mod_chanmode
*change
;
3092 unsigned char *match
;
3093 unsigned int ii
, count
;
3095 match
= alloca(bans
->used
);
3098 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3100 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3107 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3109 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3116 change
= mod_chanmode_alloc(count
);
3117 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3121 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3122 change
->args
[count
++].u
.hostmask
= bans
->list
[ii
]->ban
;
3128 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3130 struct userNode
*actee
;
3136 /* may want to allow a comma delimited list of users... */
3137 if(!(actee
= GetUserH(argv
[1])))
3139 if(!is_ircmask(argv
[1]))
3141 reply("MSG_NICK_UNKNOWN", argv
[1]);
3145 mask
= strdup(argv
[1]);
3148 /* We don't sanitize the mask here because ircu
3150 if(action
& ACTION_UNBAN
)
3152 struct mod_chanmode
*change
;
3153 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3156 modcmd_chanmode_announce(change
);
3157 mod_chanmode_free(change
);
3162 if(action
& ACTION_DEL_BAN
)
3164 struct banData
*ban
, *next
;
3166 ban
= channel
->channel_info
->bans
;
3170 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3173 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3178 del_channel_ban(ban
);
3185 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3187 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3193 static CHANSERV_FUNC(cmd_unban
)
3195 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3198 static CHANSERV_FUNC(cmd_delban
)
3200 /* it doesn't necessarily have to remove the channel ban - may want
3201 to make that an option. */
3202 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3205 static CHANSERV_FUNC(cmd_unbanme
)
3207 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3208 long flags
= ACTION_UNBAN
;
3210 /* remove permanent bans if the user has the proper access. */
3211 if(uData
->access
>= UL_MANAGER
)
3212 flags
|= ACTION_DEL_BAN
;
3214 argv
[1] = user
->nick
;
3215 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3218 static CHANSERV_FUNC(cmd_unbanall
)
3220 struct mod_chanmode
*change
;
3223 if(!channel
->banlist
.used
)
3225 reply("CSMSG_NO_BANS", channel
->name
);
3229 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3230 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3232 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3233 change
->args
[ii
].u
.hostmask
= channel
->banlist
.list
[ii
]->ban
;
3235 modcmd_chanmode_announce(change
);
3236 mod_chanmode_free(change
);
3237 reply("CSMSG_BANS_REMOVED", channel
->name
);
3241 static CHANSERV_FUNC(cmd_open
)
3243 struct mod_chanmode
*change
;
3245 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3247 change
= mod_chanmode_alloc(0);
3248 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3249 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3250 && channel
->channel_info
->modes
.modes_set
)
3251 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3252 modcmd_chanmode_announce(change
);
3253 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3254 mod_chanmode_free(change
);
3258 static CHANSERV_FUNC(cmd_myaccess
)
3260 static struct string_buffer sbuf
;
3261 struct handle_info
*target_handle
;
3262 struct userData
*uData
;
3265 target_handle
= user
->handle_info
;
3266 else if(!IsHelping(user
))
3268 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3271 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3274 if(!target_handle
->channels
)
3276 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3280 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3281 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3283 struct chanData
*cData
= uData
->channel
;
3285 if(uData
->access
> UL_OWNER
)
3287 if(IsProtected(cData
)
3288 && (target_handle
!= user
->handle_info
)
3289 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3292 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3293 if(uData
->flags
!= USER_AUTO_OP
)
3294 string_buffer_append(&sbuf
, ',');
3295 if(IsUserSuspended(uData
))
3296 string_buffer_append(&sbuf
, 's');
3297 if(IsUserAutoOp(uData
))
3299 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
3300 string_buffer_append(&sbuf
, 'o');
3301 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
3302 string_buffer_append(&sbuf
, 'h');
3303 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
3304 string_buffer_append(&sbuf
, 'v');
3306 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3307 string_buffer_append(&sbuf
, 'i');
3309 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3311 string_buffer_append_string(&sbuf
, ")]");
3312 string_buffer_append(&sbuf
, '\0');
3313 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3319 static CHANSERV_FUNC(cmd_access
)
3321 struct userNode
*target
;
3322 struct handle_info
*target_handle
;
3323 struct userData
*uData
;
3325 char prefix
[MAXLEN
];
3330 target_handle
= target
->handle_info
;
3332 else if((target
= GetUserH(argv
[1])))
3334 target_handle
= target
->handle_info
;
3336 else if(argv
[1][0] == '*')
3338 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3340 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3346 reply("MSG_NICK_UNKNOWN", argv
[1]);
3350 assert(target
|| target_handle
);
3352 if(target
== chanserv
)
3354 reply("CSMSG_IS_CHANSERV");
3362 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3367 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3370 reply("MSG_AUTHENTICATE");
3376 const char *epithet
= NULL
, *type
= NULL
;
3379 epithet
= chanserv_conf
.irc_operator_epithet
;
3382 else if(IsNetworkHelper(target
))
3384 epithet
= chanserv_conf
.network_helper_epithet
;
3385 type
= "network helper";
3387 else if(IsSupportHelper(target
))
3389 epithet
= chanserv_conf
.support_helper_epithet
;
3390 type
= "support helper";
3394 if(target_handle
->epithet
)
3395 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3397 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3399 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3403 sprintf(prefix
, "%s", target_handle
->handle
);
3406 if(!channel
->channel_info
)
3408 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3412 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3413 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3414 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3416 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, uData
->access
, channel
->name
);
3417 /* To prevent possible information leaks, only show infolines
3418 * if the requestor is in the channel or it's their own
3420 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3422 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3424 /* Likewise, only say it's suspended if the user has active
3425 * access in that channel or it's their own entry. */
3426 if(IsUserSuspended(uData
)
3427 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3428 || (user
->handle_info
== uData
->handle
)))
3430 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3435 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3442 zoot_list(struct listData
*list
)
3444 struct userData
*uData
;
3445 unsigned int start
, curr
, highest
, lowest
;
3446 struct helpfile_table tmp_table
;
3447 const char **temp
, *msg
;
3449 if(list
->table
.length
== 1)
3452 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3454 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3455 msg
= user_find_message(list
->user
, "MSG_NONE");
3456 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3458 tmp_table
.width
= list
->table
.width
;
3459 tmp_table
.flags
= list
->table
.flags
;
3460 list
->table
.contents
[0][0] = " ";
3461 highest
= list
->highest
;
3462 if(list
->lowest
!= 0)
3463 lowest
= list
->lowest
;
3464 else if(highest
< 100)
3467 lowest
= highest
- 100;
3468 for(start
= curr
= 1; curr
< list
->table
.length
; )
3470 uData
= list
->users
[curr
-1];
3471 list
->table
.contents
[curr
++][0] = " ";
3472 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3475 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, lowest
, highest
, list
->search
);
3477 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, lowest
, highest
);
3478 temp
= list
->table
.contents
[--start
];
3479 list
->table
.contents
[start
] = list
->table
.contents
[0];
3480 tmp_table
.contents
= list
->table
.contents
+ start
;
3481 tmp_table
.length
= curr
- start
;
3482 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3483 list
->table
.contents
[start
] = temp
;
3485 highest
= lowest
- 1;
3486 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3492 def_list(struct listData
*list
)
3496 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3498 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3499 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3500 if(list
->table
.length
== 1)
3502 msg
= user_find_message(list
->user
, "MSG_NONE");
3503 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3508 userData_access_comp(const void *arg_a
, const void *arg_b
)
3510 const struct userData
*a
= *(struct userData
**)arg_a
;
3511 const struct userData
*b
= *(struct userData
**)arg_b
;
3513 if(a
->access
!= b
->access
)
3514 res
= b
->access
- a
->access
;
3516 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3521 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3523 void (*send_list
)(struct listData
*);
3524 struct userData
*uData
;
3525 struct listData lData
;
3526 unsigned int matches
;
3530 lData
.bot
= cmd
->parent
->bot
;
3531 lData
.channel
= channel
;
3532 lData
.lowest
= lowest
;
3533 lData
.highest
= highest
;
3534 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3535 send_list
= def_list
;
3536 (void)zoot_list
; /* since it doesn't show user levels */
3538 if(user
->handle_info
)
3540 switch(user
->handle_info
->userlist_style
)
3542 case HI_STYLE_DEF
: send_list
= def_list
; break;
3543 case HI_STYLE_ZOOT
: send_list
= def_list
; break;
3547 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3549 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3551 if((uData
->access
< lowest
)
3552 || (uData
->access
> highest
)
3553 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3555 lData
.users
[matches
++] = uData
;
3557 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3559 lData
.table
.length
= matches
+1;
3560 lData
.table
.width
= 4;
3561 lData
.table
.flags
= TABLE_NO_FREE
;
3562 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3563 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3564 lData
.table
.contents
[0] = ary
;
3567 ary
[2] = "Last Seen";
3569 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3571 struct userData
*uData
= lData
.users
[matches
-1];
3572 char seen
[INTERVALLEN
];
3574 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3575 lData
.table
.contents
[matches
] = ary
;
3576 ary
[0] = strtab(uData
->access
);
3577 ary
[1] = uData
->handle
->handle
;
3580 else if(!uData
->seen
)
3583 ary
[2] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3584 ary
[2] = strdup(ary
[2]);
3585 if(IsUserSuspended(uData
))
3586 ary
[3] = "Suspended";
3587 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3588 ary
[3] = "Vacation";
3593 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3595 free((char*)lData
.table
.contents
[matches
][2]);
3596 free(lData
.table
.contents
[matches
]);
3598 free(lData
.table
.contents
[0]);
3599 free(lData
.table
.contents
);
3603 static CHANSERV_FUNC(cmd_users
)
3605 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3608 static CHANSERV_FUNC(cmd_wlist
)
3610 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3613 static CHANSERV_FUNC(cmd_clist
)
3615 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3618 static CHANSERV_FUNC(cmd_mlist
)
3620 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3623 static CHANSERV_FUNC(cmd_olist
)
3625 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3628 static CHANSERV_FUNC(cmd_hlist
)
3630 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3633 static CHANSERV_FUNC(cmd_plist
)
3635 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3638 static CHANSERV_FUNC(cmd_bans
)
3640 struct helpfile_table tbl
;
3641 unsigned int matches
= 0, timed
= 0, ii
;
3642 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3643 const char *msg_never
, *triggered
, *expires
;
3644 struct banData
*ban
, **bans
;
3651 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3653 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3655 if(search
&& !match_ircglobs(search
, ban
->mask
))
3657 bans
[matches
++] = ban
;
3662 tbl
.length
= matches
+ 1;
3663 tbl
.width
= 4 + timed
;
3665 tbl
.flags
= TABLE_NO_FREE
;
3666 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3667 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3668 tbl
.contents
[0][0] = "Mask";
3669 tbl
.contents
[0][1] = "Set By";
3670 tbl
.contents
[0][2] = "Triggered";
3673 tbl
.contents
[0][3] = "Expires";
3674 tbl
.contents
[0][4] = "Reason";
3677 tbl
.contents
[0][3] = "Reason";
3680 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3682 free(tbl
.contents
[0]);
3687 msg_never
= user_find_message(user
, "MSG_NEVER");
3688 for(ii
= 0; ii
< matches
; )
3694 else if(ban
->expires
)
3695 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3697 expires
= msg_never
;
3700 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3702 triggered
= msg_never
;
3704 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3705 tbl
.contents
[ii
][0] = ban
->mask
;
3706 tbl
.contents
[ii
][1] = ban
->owner
;
3707 tbl
.contents
[ii
][2] = strdup(triggered
);
3710 tbl
.contents
[ii
][3] = strdup(expires
);
3711 tbl
.contents
[ii
][4] = ban
->reason
;
3714 tbl
.contents
[ii
][3] = ban
->reason
;
3716 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3717 reply("MSG_MATCH_COUNT", matches
);
3718 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3720 free((char*)tbl
.contents
[ii
][2]);
3722 free((char*)tbl
.contents
[ii
][3]);
3723 free(tbl
.contents
[ii
]);
3725 free(tbl
.contents
[0]);
3731 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3733 struct chanData
*cData
= channel
->channel_info
;
3734 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3736 if(cData
->topic_mask
)
3737 return !match_ircglob(new_topic
, cData
->topic_mask
);
3738 else if(cData
->topic
)
3739 return irccasecmp(new_topic
, cData
->topic
);
3744 static CHANSERV_FUNC(cmd_topic
)
3746 struct chanData
*cData
;
3749 cData
= channel
->channel_info
;
3754 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
3755 reply("CSMSG_TOPIC_SET", cData
->topic
);
3759 reply("CSMSG_NO_TOPIC", channel
->name
);
3763 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
3764 /* If they say "!topic *", use an empty topic. */
3765 if((topic
[0] == '*') && (topic
[1] == 0))
3767 if(bad_topic(channel
, user
, topic
))
3769 char *topic_mask
= cData
->topic_mask
;
3772 char new_topic
[TOPICLEN
+1], tchar
;
3773 int pos
=0, starpos
=-1, dpos
=0, len
;
3775 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3782 len
= strlen(topic
);
3783 if((dpos
+ len
) > TOPICLEN
)
3784 len
= TOPICLEN
+ 1 - dpos
;
3785 memcpy(new_topic
+dpos
, topic
, len
);
3789 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
3790 default: new_topic
[dpos
++] = tchar
; break;
3793 if((dpos
> TOPICLEN
) || tchar
)
3796 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
3797 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
3800 new_topic
[dpos
] = 0;
3801 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
3803 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
3808 SetChannelTopic(channel
, chanserv
, topic
, 1);
3810 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
3812 /* Grab the topic and save it as the default topic. */
3814 cData
->topic
= strdup(channel
->topic
);
3820 static CHANSERV_FUNC(cmd_mode
)
3822 struct mod_chanmode
*change
;
3826 change
= &channel
->channel_info
->modes
;
3827 if(change
->modes_set
|| change
->modes_clear
) {
3828 modcmd_chanmode_announce(change
);
3829 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
3831 reply("CSMSG_NO_MODES", channel
->name
);
3835 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
3838 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
3842 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3843 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
3846 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
3847 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
3851 modcmd_chanmode_announce(change
);
3852 mod_chanmode_free(change
);
3853 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
3857 static CHANSERV_FUNC(cmd_invite
)
3859 struct userData
*uData
;
3860 struct userNode
*invite
;
3862 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3866 if(!(invite
= GetUserH(argv
[1])))
3868 reply("MSG_NICK_UNKNOWN", argv
[1]);
3875 if(GetUserMode(channel
, invite
))
3877 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
3885 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
3886 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
3889 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
3891 irc_invite(chanserv
, invite
, channel
);
3893 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
3898 static CHANSERV_FUNC(cmd_inviteme
)
3900 if(GetUserMode(channel
, user
))
3902 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
3905 if(channel
->channel_info
3906 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
3908 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
3911 irc_invite(cmd
->parent
->bot
, user
, channel
);
3916 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
3919 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
3921 /* We display things based on two dimensions:
3922 * - Issue time: present or absent
3923 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3924 * (in order of precedence, so something both expired and revoked
3925 * only counts as revoked)
3927 combo
= (suspended
->issued
? 4 : 0)
3928 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
3930 case 0: /* no issue time, indefinite expiration */
3931 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
3933 case 1: /* no issue time, expires in future */
3934 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
3935 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
3937 case 2: /* no issue time, expired */
3938 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
3939 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
3941 case 3: /* no issue time, revoked */
3942 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
3943 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
3945 case 4: /* issue time set, indefinite expiration */
3946 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3947 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
3949 case 5: /* issue time set, expires in future */
3950 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3951 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
3952 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3954 case 6: /* issue time set, expired */
3955 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3956 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
3957 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3959 case 7: /* issue time set, revoked */
3960 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3961 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
3962 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3965 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
3970 static CHANSERV_FUNC(cmd_info
)
3972 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
3973 struct userData
*uData
, *owner
;
3974 struct chanData
*cData
;
3975 struct do_not_register
*dnr
;
3980 cData
= channel
->channel_info
;
3981 reply("CSMSG_CHANNEL_INFO", channel
->name
);
3983 uData
= GetChannelUser(cData
, user
->handle_info
);
3984 if(uData
&& (uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
3986 mod_chanmode_format(&cData
->modes
, modes
);
3987 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
3988 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
3991 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
3995 note
= iter_data(it
);
3996 if(!note_type_visible_to_user(cData
, note
->type
, user
))
3999 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4000 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4003 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4004 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4005 if(owner
->access
== UL_OWNER
)
4006 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4007 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4008 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
4009 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4010 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4012 privileged
= IsStaff(user
);
4013 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4014 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4016 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4017 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4019 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4021 struct suspended
*suspended
;
4022 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4023 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4024 show_suspension_info(cmd
, user
, suspended
);
4026 else if(IsSuspended(cData
))
4028 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4029 show_suspension_info(cmd
, user
, cData
->suspended
);
4034 static CHANSERV_FUNC(cmd_netinfo
)
4036 extern time_t boot_time
;
4037 extern unsigned long burst_length
;
4038 char interval
[INTERVALLEN
];
4040 reply("CSMSG_NETWORK_INFO");
4041 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4042 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4043 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4044 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4045 reply("CSMSG_NETWORK_BANS", banCount
);
4046 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4047 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4048 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4053 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4055 struct helpfile_table table
;
4057 struct userNode
*user
;
4062 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4063 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4064 for(nn
=0; nn
<list
->used
; nn
++)
4066 user
= list
->list
[nn
];
4067 if(user
->modes
& skip_flags
)
4071 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4074 nick
= alloca(strlen(user
->nick
)+3);
4075 sprintf(nick
, "(%s)", user
->nick
);
4079 table
.contents
[table
.length
][0] = nick
;
4082 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4085 static CHANSERV_FUNC(cmd_ircops
)
4087 reply("CSMSG_STAFF_OPERS");
4088 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4092 static CHANSERV_FUNC(cmd_helpers
)
4094 reply("CSMSG_STAFF_HELPERS");
4095 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4099 static CHANSERV_FUNC(cmd_staff
)
4101 reply("CSMSG_NETWORK_STAFF");
4102 cmd_ircops(CSFUNC_ARGS
);
4103 cmd_helpers(CSFUNC_ARGS
);
4107 static CHANSERV_FUNC(cmd_peek
)
4109 struct modeNode
*mn
;
4110 char modes
[MODELEN
];
4112 struct helpfile_table table
;
4114 irc_make_chanmode(channel
, modes
);
4116 reply("CSMSG_PEEK_INFO", channel
->name
);
4117 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4118 reply("CSMSG_PEEK_MODES", modes
);
4119 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4123 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4124 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4125 for(n
= 0; n
< channel
->members
.used
; n
++)
4127 mn
= channel
->members
.list
[n
];
4128 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4130 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4131 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4136 reply("CSMSG_PEEK_OPS");
4137 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4140 reply("CSMSG_PEEK_NO_OPS");
4144 static MODCMD_FUNC(cmd_wipeinfo
)
4146 struct handle_info
*victim
;
4147 struct userData
*ud
, *actor
;
4150 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4151 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4153 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4155 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4158 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4160 reply("MSG_USER_OUTRANKED", victim
->handle
);
4166 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4170 static CHANSERV_FUNC(cmd_resync
)
4172 struct mod_chanmode
*changes
;
4173 struct chanData
*cData
= channel
->channel_info
;
4174 unsigned int ii
, used
;
4176 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4177 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4179 struct modeNode
*mn
= channel
->members
.list
[ii
];
4180 struct userData
*uData
;
4182 if(IsService(mn
->user
))
4185 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4186 if(!cData
->lvlOpts
[lvlGiveOps
]
4187 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4189 if(!(mn
->modes
& MODE_CHANOP
))
4191 changes
->args
[used
].mode
= MODE_CHANOP
;
4192 changes
->args
[used
++].u
.member
= mn
;
4195 else if(!cData
->lvlOpts
[lvlGiveHalfOps
]
4196 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
]))
4198 if(!(mn
->modes
& MODE_HALFOP
))
4200 changes
->args
[used
].mode
= MODE_HALFOP
;
4201 changes
->args
[used
++].u
.member
= mn
;
4203 if(mn
->modes
& MODE_CHANOP
)
4205 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4206 changes
->args
[used
++].u
.member
= mn
;
4208 if(mn
->modes
& MODE_VOICE
)
4210 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4211 changes
->args
[used
++].u
.member
= mn
;
4214 else if(!cData
->lvlOpts
[lvlGiveVoice
]
4215 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
]))
4217 if(mn
->modes
& MODE_CHANOP
)
4219 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4220 changes
->args
[used
++].u
.member
= mn
;
4222 if(mn
->modes
& MODE_HALFOP
)
4224 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4225 changes
->args
[used
++].u
.member
= mn
;
4227 if(!(mn
->modes
& MODE_VOICE
))
4229 changes
->args
[used
].mode
= MODE_VOICE
;
4230 changes
->args
[used
++].u
.member
= mn
;
4237 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4238 changes
->args
[used
++].u
.member
= mn
;
4242 changes
->argc
= used
;
4243 modcmd_chanmode_announce(changes
);
4244 mod_chanmode_free(changes
);
4245 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4249 static CHANSERV_FUNC(cmd_seen
)
4251 struct userData
*uData
;
4252 struct handle_info
*handle
;
4253 char seen
[INTERVALLEN
];
4257 if(!irccasecmp(argv
[1], chanserv
->nick
))
4259 reply("CSMSG_IS_CHANSERV");
4263 if(!(handle
= get_handle_info(argv
[1])))
4265 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4269 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4271 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4276 reply("CSMSG_USER_PRESENT", handle
->handle
);
4277 else if(uData
->seen
)
4278 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4280 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4282 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4283 reply("CSMSG_USER_VACATION", handle
->handle
);
4288 static MODCMD_FUNC(cmd_names
)
4290 struct userNode
*targ
;
4291 struct userData
*targData
;
4292 unsigned int ii
, pos
;
4295 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4297 targ
= channel
->members
.list
[ii
]->user
;
4298 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4301 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4304 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4308 if(IsUserSuspended(targData
))
4310 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4313 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4314 reply("CSMSG_END_NAMES", channel
->name
);
4319 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4321 switch(ntype
->visible_type
)
4323 case NOTE_VIS_ALL
: return 1;
4324 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4325 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4330 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4332 struct userData
*uData
;
4334 switch(ntype
->set_access_type
)
4336 case NOTE_SET_CHANNEL_ACCESS
:
4337 if(!user
->handle_info
)
4339 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4341 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4342 case NOTE_SET_CHANNEL_SETTER
:
4343 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4344 case NOTE_SET_PRIVILEGED
: default:
4345 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4349 static CHANSERV_FUNC(cmd_note
)
4351 struct chanData
*cData
;
4353 struct note_type
*ntype
;
4355 cData
= channel
->channel_info
;
4358 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4362 /* If no arguments, show all visible notes for the channel. */
4368 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4370 note
= iter_data(it
);
4371 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4374 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4375 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4378 reply("CSMSG_NOTELIST_END", channel
->name
);
4380 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4382 /* If one argument, show the named note. */
4385 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4386 && note_type_visible_to_user(cData
, note
->type
, user
))
4388 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4390 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4391 && note_type_visible_to_user(NULL
, ntype
, user
))
4393 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4398 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4402 /* Assume they're trying to set a note. */
4406 ntype
= dict_find(note_types
, argv
[1], NULL
);
4409 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4412 else if(note_type_settable_by_user(channel
, ntype
, user
))
4414 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4415 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4416 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4417 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4418 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4420 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4422 /* The note is viewable to staff only, so return 0
4423 to keep the invocation from getting logged (or
4424 regular users can see it in !events). */
4430 reply("CSMSG_NO_ACCESS");
4437 static CHANSERV_FUNC(cmd_delnote
)
4442 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4443 || !note_type_settable_by_user(channel
, note
->type
, user
))
4445 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4448 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4449 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4453 static CHANSERV_FUNC(cmd_events
)
4455 struct logSearch discrim
;
4456 struct logReport report
;
4457 unsigned int matches
, limit
;
4459 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4460 if(limit
< 1 || limit
> 200)
4463 memset(&discrim
, 0, sizeof(discrim
));
4464 discrim
.masks
.bot
= chanserv
;
4465 discrim
.masks
.channel_name
= channel
->name
;
4467 discrim
.masks
.command
= argv
[2];
4468 discrim
.limit
= limit
;
4469 discrim
.max_time
= INT_MAX
;
4470 discrim
.severities
= 1 << LOG_COMMAND
;
4471 report
.reporter
= chanserv
;
4473 reply("CSMSG_EVENT_SEARCH_RESULTS");
4474 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4476 reply("MSG_MATCH_COUNT", matches
);
4478 reply("MSG_NO_MATCHES");
4482 static CHANSERV_FUNC(cmd_say
)
4488 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4489 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4491 else if(GetUserH(argv
[1]))
4494 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4495 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4499 reply("MSG_NOT_TARGET_NAME");
4505 static CHANSERV_FUNC(cmd_emote
)
4511 /* CTCP is so annoying. */
4512 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4513 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4515 else if(GetUserH(argv
[1]))
4517 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4518 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4522 reply("MSG_NOT_TARGET_NAME");
4528 struct channelList
*
4529 chanserv_support_channels(void)
4531 return &chanserv_conf
.support_channels
;
4534 static CHANSERV_FUNC(cmd_expire
)
4536 int channel_count
= registered_channels
;
4537 expire_channels(NULL
);
4538 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4543 chanserv_expire_suspension(void *data
)
4545 struct suspended
*suspended
= data
;
4546 struct chanNode
*channel
;
4547 struct mod_chanmode change
;
4549 if(!suspended
->expires
|| (now
< suspended
->expires
))
4550 suspended
->revoked
= now
;
4551 channel
= suspended
->cData
->channel
;
4552 suspended
->cData
->channel
= channel
;
4553 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4554 mod_chanmode_init(&change
);
4556 change
.args
[0].mode
= MODE_CHANOP
;
4557 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4558 mod_chanmode_announce(chanserv
, channel
, &change
);
4561 static CHANSERV_FUNC(cmd_csuspend
)
4563 struct suspended
*suspended
;
4564 char reason
[MAXLEN
];
4565 time_t expiry
, duration
;
4566 struct userData
*uData
;
4570 if(IsProtected(channel
->channel_info
))
4572 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4576 if(argv
[1][0] == '!')
4578 else if(IsSuspended(channel
->channel_info
))
4580 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4581 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4585 if(!strcmp(argv
[1], "0"))
4587 else if((duration
= ParseInterval(argv
[1])))
4588 expiry
= now
+ duration
;
4591 reply("MSG_INVALID_DURATION", argv
[1]);
4595 unsplit_string(argv
+ 2, argc
- 2, reason
);
4597 suspended
= calloc(1, sizeof(*suspended
));
4598 suspended
->revoked
= 0;
4599 suspended
->issued
= now
;
4600 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4601 suspended
->expires
= expiry
;
4602 suspended
->reason
= strdup(reason
);
4603 suspended
->cData
= channel
->channel_info
;
4604 suspended
->previous
= suspended
->cData
->suspended
;
4605 suspended
->cData
->suspended
= suspended
;
4607 if(suspended
->expires
)
4608 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4610 if(IsSuspended(channel
->channel_info
))
4612 suspended
->previous
->revoked
= now
;
4613 if(suspended
->previous
->expires
)
4614 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4615 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4616 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4620 /* Mark all users in channel as absent. */
4621 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4630 /* Mark the channel as suspended, then part. */
4631 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4632 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4633 reply("CSMSG_SUSPENDED", channel
->name
);
4634 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4635 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4640 static CHANSERV_FUNC(cmd_cunsuspend
)
4642 struct suspended
*suspended
;
4643 char message
[MAXLEN
];
4645 if(!IsSuspended(channel
->channel_info
))
4647 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4651 suspended
= channel
->channel_info
->suspended
;
4653 /* Expire the suspension and join ChanServ to the channel. */
4654 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4655 chanserv_expire_suspension(suspended
);
4656 reply("CSMSG_UNSUSPENDED", channel
->name
);
4657 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4658 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4662 typedef struct chanservSearch
4670 unsigned long flags
;
4674 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4677 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4682 search
= malloc(sizeof(struct chanservSearch
));
4683 memset(search
, 0, sizeof(*search
));
4686 for(i
= 0; i
< argc
; i
++)
4688 /* Assume all criteria require arguments. */
4691 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4695 if(!irccasecmp(argv
[i
], "name"))
4696 search
->name
= argv
[++i
];
4697 else if(!irccasecmp(argv
[i
], "registrar"))
4698 search
->registrar
= argv
[++i
];
4699 else if(!irccasecmp(argv
[i
], "unvisited"))
4700 search
->unvisited
= ParseInterval(argv
[++i
]);
4701 else if(!irccasecmp(argv
[i
], "registered"))
4702 search
->registered
= ParseInterval(argv
[++i
]);
4703 else if(!irccasecmp(argv
[i
], "flags"))
4706 if(!irccasecmp(argv
[i
], "nodelete"))
4707 search
->flags
|= CHANNEL_NODELETE
;
4708 else if(!irccasecmp(argv
[i
], "suspended"))
4709 search
->flags
|= CHANNEL_SUSPENDED
;
4712 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4716 else if(!irccasecmp(argv
[i
], "limit"))
4717 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4720 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4725 if(search
->name
&& !strcmp(search
->name
, "*"))
4727 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4728 search
->registrar
= 0;
4737 chanserv_channel_match(struct chanData
*channel
, search_t search
)
4739 const char *name
= channel
->channel
->name
;
4740 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
4741 (search
->registrar
&& !channel
->registrar
) ||
4742 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
4743 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
4744 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
4745 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
4752 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
4754 struct chanData
*channel
;
4755 unsigned int matches
= 0;
4757 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
4759 if(!chanserv_channel_match(channel
, search
))
4769 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
4774 search_print(struct chanData
*channel
, void *data
)
4776 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
4779 static CHANSERV_FUNC(cmd_search
)
4782 unsigned int matches
;
4783 channel_search_func action
;
4787 if(!irccasecmp(argv
[1], "count"))
4788 action
= search_count
;
4789 else if(!irccasecmp(argv
[1], "print"))
4790 action
= search_print
;
4793 reply("CSMSG_ACTION_INVALID", argv
[1]);
4797 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
4801 if(action
== search_count
)
4802 search
->limit
= INT_MAX
;
4804 if(action
== search_print
)
4805 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4807 matches
= chanserv_channel_search(search
, action
, user
);
4810 reply("MSG_MATCH_COUNT", matches
);
4812 reply("MSG_NO_MATCHES");
4818 static CHANSERV_FUNC(cmd_unvisited
)
4820 struct chanData
*cData
;
4821 time_t interval
= chanserv_conf
.channel_expire_delay
;
4822 char buffer
[INTERVALLEN
];
4823 unsigned int limit
= 25, matches
= 0;
4827 interval
= ParseInterval(argv
[1]);
4829 limit
= atoi(argv
[2]);
4832 intervalString(buffer
, interval
, user
->handle_info
);
4833 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
4835 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
4837 if((now
- cData
->visited
) < interval
)
4840 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
4841 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
4848 static MODCMD_FUNC(chan_opt_defaulttopic
)
4854 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4856 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4860 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
4862 free(channel
->channel_info
->topic
);
4863 if(topic
[0] == '*' && topic
[1] == 0)
4865 topic
= channel
->channel_info
->topic
= NULL
;
4869 topic
= channel
->channel_info
->topic
= strdup(topic
);
4870 if(channel
->channel_info
->topic_mask
4871 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
4872 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4874 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
4877 if(channel
->channel_info
->topic
)
4878 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
4880 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
4884 static MODCMD_FUNC(chan_opt_topicmask
)
4888 struct chanData
*cData
= channel
->channel_info
;
4891 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4893 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4897 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
4899 if(cData
->topic_mask
)
4900 free(cData
->topic_mask
);
4901 if(mask
[0] == '*' && mask
[1] == 0)
4903 cData
->topic_mask
= 0;
4907 cData
->topic_mask
= strdup(mask
);
4909 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
4910 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
4911 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4915 if(channel
->channel_info
->topic_mask
)
4916 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
4918 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
4922 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
4926 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
4930 if(greeting
[0] == '*' && greeting
[1] == 0)
4934 unsigned int length
= strlen(greeting
);
4935 if(length
> chanserv_conf
.greeting_length
)
4937 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
4940 *data
= strdup(greeting
);
4949 reply(name
, user_find_message(user
, "MSG_NONE"));
4953 static MODCMD_FUNC(chan_opt_greeting
)
4955 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
4958 static MODCMD_FUNC(chan_opt_usergreeting
)
4960 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
4963 static MODCMD_FUNC(chan_opt_modes
)
4965 struct mod_chanmode
*new_modes
;
4966 char modes
[MODELEN
];
4970 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
4972 reply("CSMSG_NO_ACCESS");
4975 if(argv
[1][0] == '*' && argv
[1][1] == 0)
4977 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
4979 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
4981 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
4984 else if(new_modes
->argc
> 1)
4986 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
4987 mod_chanmode_free(new_modes
);
4992 channel
->channel_info
->modes
= *new_modes
;
4993 modcmd_chanmode_announce(new_modes
);
4994 mod_chanmode_free(new_modes
);
4998 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5000 reply("CSMSG_SET_MODES", modes
);
5002 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5006 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5008 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5010 struct chanData
*cData
= channel
->channel_info
;
5015 /* Set flag according to value. */
5016 if(enabled_string(argv
[1]))
5018 cData
->flags
|= mask
;
5021 else if(disabled_string(argv
[1]))
5023 cData
->flags
&= ~mask
;
5028 reply("MSG_INVALID_BINARY", argv
[1]);
5034 /* Find current option value. */
5035 value
= (cData
->flags
& mask
) ? 1 : 0;
5039 reply(name
, user_find_message(user
, "MSG_ON"));
5041 reply(name
, user_find_message(user
, "MSG_OFF"));
5045 static MODCMD_FUNC(chan_opt_nodelete
)
5047 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5049 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5053 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5056 static MODCMD_FUNC(chan_opt_dynlimit
)
5058 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5061 static MODCMD_FUNC(chan_opt_offchannel
)
5063 struct chanData
*cData
= channel
->channel_info
;
5068 /* Set flag according to value. */
5069 if(enabled_string(argv
[1]))
5071 if(!IsOffChannel(cData
))
5072 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5073 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5076 else if(disabled_string(argv
[1]))
5078 if(IsOffChannel(cData
))
5080 struct mod_chanmode change
;
5081 mod_chanmode_init(&change
);
5083 change
.args
[0].mode
= MODE_CHANOP
;
5084 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5085 mod_chanmode_announce(chanserv
, channel
, &change
);
5087 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5092 reply("MSG_INVALID_BINARY", argv
[1]);
5098 /* Find current option value. */
5099 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5103 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5105 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5109 static MODCMD_FUNC(chan_opt_defaults
)
5111 struct userData
*uData
;
5112 struct chanData
*cData
;
5113 const char *confirm
;
5114 enum levelOption lvlOpt
;
5115 enum charOption chOpt
;
5117 cData
= channel
->channel_info
;
5118 uData
= GetChannelUser(cData
, user
->handle_info
);
5119 if(!uData
|| (uData
->access
< UL_OWNER
))
5121 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5124 confirm
= make_confirmation_string(uData
);
5125 if((argc
< 2) || strcmp(argv
[1], confirm
))
5127 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5130 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5131 cData
->modes
= chanserv_conf
.default_modes
;
5132 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5133 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5134 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5135 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5136 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5141 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5143 struct chanData
*cData
= channel
->channel_info
;
5144 struct userData
*uData
;
5145 unsigned short value
;
5149 if(!check_user_level(channel
, user
, option
, 1, 1))
5151 reply("CSMSG_CANNOT_SET");
5154 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5155 if(!value
&& strcmp(argv
[1], "0"))
5157 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5160 uData
= GetChannelUser(cData
, user
->handle_info
);
5161 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5163 reply("CSMSG_BAD_SETLEVEL");
5169 if(value
> cData
->lvlOpts
[lvlGiveOps
])
5171 reply("CSMSG_BAD_GIVEVOICE", cData
->lvlOpts
[lvlGiveOps
]);
5175 case lvlGiveHalfOps
:
5176 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5178 reply("CSMSG_BAD_GIVEHOPS", cData
->lvlOpts
[lvlGiveHalfOps
]);
5183 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5185 reply("CSMSG_BAD_GIVEOPS", cData
->lvlOpts
[lvlGiveVoice
]);
5190 /* This test only applies to owners, since non-owners
5191 * trying to set an option to above their level get caught
5192 * by the CSMSG_BAD_SETLEVEL test above.
5194 if(value
> uData
->access
)
5196 reply("CSMSG_BAD_SETTERS");
5203 cData
->lvlOpts
[option
] = value
;
5205 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5209 static MODCMD_FUNC(chan_opt_enfops
)
5211 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5214 static MODCMD_FUNC(chan_opt_enfhalfops
)
5216 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5219 static MODCMD_FUNC(chan_opt_giveops
)
5221 return channel_level_option(lvlGiveOps
, CSFUNC_ARGS
);
5224 static MODCMD_FUNC(chan_opt_givehalfops
)
5226 return channel_level_option(lvlGiveHalfOps
, CSFUNC_ARGS
);
5229 static MODCMD_FUNC(chan_opt_enfmodes
)
5231 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5234 static MODCMD_FUNC(chan_opt_enftopic
)
5236 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5239 static MODCMD_FUNC(chan_opt_pubcmd
)
5241 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5244 static MODCMD_FUNC(chan_opt_setters
)
5246 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5249 static MODCMD_FUNC(chan_opt_ctcpusers
)
5251 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5254 static MODCMD_FUNC(chan_opt_userinfo
)
5256 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5259 static MODCMD_FUNC(chan_opt_givevoice
)
5261 return channel_level_option(lvlGiveVoice
, CSFUNC_ARGS
);
5264 static MODCMD_FUNC(chan_opt_topicsnarf
)
5266 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5269 static MODCMD_FUNC(chan_opt_inviteme
)
5271 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5275 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5277 struct chanData
*cData
= channel
->channel_info
;
5278 int count
= charOptions
[option
].count
, index
;
5282 index
= atoi(argv
[1]);
5284 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5286 reply("CSMSG_INVALID_NUMERIC", index
);
5287 /* Show possible values. */
5288 for(index
= 0; index
< count
; index
++)
5289 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5293 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5297 /* Find current option value. */
5300 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5304 /* Somehow, the option value is corrupt; reset it to the default. */
5305 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5310 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5314 static MODCMD_FUNC(chan_opt_protect
)
5316 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5319 static MODCMD_FUNC(chan_opt_toys
)
5321 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5324 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5326 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5329 static MODCMD_FUNC(chan_opt_topicrefresh
)
5331 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5334 static struct svccmd_list set_shows_list
;
5337 handle_svccmd_unbind(struct svccmd
*target
) {
5339 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5340 if(target
== set_shows_list
.list
[ii
])
5341 set_shows_list
.used
= 0;
5344 static CHANSERV_FUNC(cmd_set
)
5346 struct svccmd
*subcmd
;
5350 /* Check if we need to (re-)initialize set_shows_list. */
5351 if(!set_shows_list
.used
)
5353 if(!set_shows_list
.size
)
5355 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5356 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5358 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5360 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5361 sprintf(buf
, "%s %s", argv
[0], name
);
5362 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5365 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5368 svccmd_list_append(&set_shows_list
, subcmd
);
5374 reply("CSMSG_CHANNEL_OPTIONS");
5375 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5377 subcmd
= set_shows_list
.list
[ii
];
5378 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5383 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5384 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5387 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5390 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5392 reply("CSMSG_NO_ACCESS");
5396 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5400 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5402 struct userData
*uData
;
5404 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5407 reply("CSMSG_NOT_USER", channel
->name
);
5413 /* Just show current option value. */
5415 else if(enabled_string(argv
[1]))
5417 uData
->flags
|= mask
;
5419 else if(disabled_string(argv
[1]))
5421 uData
->flags
&= ~mask
;
5425 reply("MSG_INVALID_BINARY", argv
[1]);
5429 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5433 static MODCMD_FUNC(user_opt_noautoop
)
5435 struct userData
*uData
;
5437 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5440 reply("CSMSG_NOT_USER", channel
->name
);
5443 if(uData
->access
< channel
->channel_info
->lvlOpts
[lvlGiveOps
])
5444 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5446 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5449 static MODCMD_FUNC(user_opt_autoinvite
)
5451 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5454 static MODCMD_FUNC(user_opt_info
)
5456 struct userData
*uData
;
5459 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5463 /* If they got past the command restrictions (which require access)
5464 * but fail this test, we have some fool with security override on.
5466 reply("CSMSG_NOT_USER", channel
->name
);
5473 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5474 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5476 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5479 bp
= strcspn(infoline
, "\001");
5482 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5487 if(infoline
[0] == '*' && infoline
[1] == 0)
5490 uData
->info
= strdup(infoline
);
5493 reply("CSMSG_USET_INFO", uData
->info
);
5495 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5499 struct svccmd_list uset_shows_list
;
5501 static CHANSERV_FUNC(cmd_uset
)
5503 struct svccmd
*subcmd
;
5507 /* Check if we need to (re-)initialize uset_shows_list. */
5508 if(!uset_shows_list
.used
)
5512 "NoAutoOp", "AutoInvite", "Info"
5515 if(!uset_shows_list
.size
)
5517 uset_shows_list
.size
= ArrayLength(options
);
5518 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5520 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5522 const char *name
= options
[ii
];
5523 sprintf(buf
, "%s %s", argv
[0], name
);
5524 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5527 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5530 svccmd_list_append(&uset_shows_list
, subcmd
);
5536 /* Do this so options are presented in a consistent order. */
5537 reply("CSMSG_USER_OPTIONS");
5538 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5539 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5543 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5544 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5547 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5551 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5554 static CHANSERV_FUNC(cmd_giveownership
)
5556 struct handle_info
*new_owner_hi
;
5557 struct userData
*new_owner
, *curr_user
;
5558 struct chanData
*cData
= channel
->channel_info
;
5559 struct do_not_register
*dnr
;
5561 unsigned short co_access
;
5562 char reason
[MAXLEN
];
5565 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5566 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5567 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5569 struct userData
*owner
= NULL
;
5570 for(curr_user
= channel
->channel_info
->users
;
5572 curr_user
= curr_user
->next
)
5574 if(curr_user
->access
!= UL_OWNER
)
5578 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5585 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5587 char delay
[INTERVALLEN
];
5588 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5589 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5592 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5594 if(new_owner_hi
== user
->handle_info
)
5596 reply("CSMSG_NO_TRANSFER_SELF");
5599 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5602 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5605 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5607 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5610 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5611 if(!IsHelping(user
))
5612 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5614 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5617 if(new_owner
->access
>= UL_COOWNER
)
5618 co_access
= new_owner
->access
;
5620 co_access
= UL_COOWNER
;
5621 new_owner
->access
= UL_OWNER
;
5623 curr_user
->access
= co_access
;
5624 cData
->ownerTransfer
= now
;
5625 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5626 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5627 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5631 static CHANSERV_FUNC(cmd_suspend
)
5633 struct handle_info
*hi
;
5634 struct userData
*self
, *target
;
5637 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5638 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5639 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5641 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5644 if(target
->access
>= self
->access
)
5646 reply("MSG_USER_OUTRANKED", hi
->handle
);
5649 if(target
->flags
& USER_SUSPENDED
)
5651 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5656 target
->present
= 0;
5659 target
->flags
|= USER_SUSPENDED
;
5660 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5664 static CHANSERV_FUNC(cmd_unsuspend
)
5666 struct handle_info
*hi
;
5667 struct userData
*self
, *target
;
5670 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5671 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5672 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5674 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5677 if(target
->access
>= self
->access
)
5679 reply("MSG_USER_OUTRANKED", hi
->handle
);
5682 if(!(target
->flags
& USER_SUSPENDED
))
5684 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5687 target
->flags
&= ~USER_SUSPENDED
;
5688 scan_user_presence(target
, NULL
);
5689 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5693 static MODCMD_FUNC(cmd_deleteme
)
5695 struct handle_info
*hi
;
5696 struct userData
*target
;
5697 const char *confirm_string
;
5698 unsigned short access
;
5701 hi
= user
->handle_info
;
5702 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5704 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5707 if(target
->access
== UL_OWNER
)
5709 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5712 confirm_string
= make_confirmation_string(target
);
5713 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
5715 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
5718 access
= target
->access
;
5719 channel_name
= strdup(channel
->name
);
5720 del_channel_user(target
, 1);
5721 reply("CSMSG_DELETED_YOU", access
, channel_name
);
5727 chanserv_refresh_topics(UNUSED_ARG(void *data
))
5729 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
5730 struct chanData
*cData
;
5733 for(cData
= channelList
; cData
; cData
= cData
->next
)
5735 if(IsSuspended(cData
))
5737 opt
= cData
->chOpts
[chTopicRefresh
];
5740 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
5743 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
5744 cData
->last_refresh
= refresh_num
;
5746 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
5749 static CHANSERV_FUNC(cmd_unf
)
5753 char response
[MAXLEN
];
5754 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
5755 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5756 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5759 reply("CSMSG_UNF_RESPONSE");
5763 static CHANSERV_FUNC(cmd_ping
)
5767 char response
[MAXLEN
];
5768 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
5769 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5770 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5773 reply("CSMSG_PING_RESPONSE");
5777 static CHANSERV_FUNC(cmd_wut
)
5781 char response
[MAXLEN
];
5782 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
5783 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5784 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5787 reply("CSMSG_WUT_RESPONSE");
5791 static CHANSERV_FUNC(cmd_8ball
)
5793 unsigned int i
, j
, accum
;
5798 for(i
=1; i
<argc
; i
++)
5799 for(j
=0; argv
[i
][j
]; j
++)
5800 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
5801 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
5804 char response
[MAXLEN
];
5805 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
5806 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5809 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
5813 static CHANSERV_FUNC(cmd_d
)
5815 unsigned long sides
, count
, modifier
, ii
, total
;
5816 char response
[MAXLEN
], *sep
;
5820 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
5830 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
5831 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
5835 else if((sep
[0] == '-') && isdigit(sep
[1]))
5836 modifier
= strtoul(sep
, NULL
, 10);
5837 else if((sep
[0] == '+') && isdigit(sep
[1]))
5838 modifier
= strtoul(sep
+1, NULL
, 10);
5845 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
5850 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
5853 for(total
= ii
= 0; ii
< count
; ++ii
)
5854 total
+= (rand() % sides
) + 1;
5857 if((count
> 1) || modifier
)
5859 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
5860 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
5864 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
5865 sprintf(response
, fmt
, total
, sides
);
5868 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5870 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5874 static CHANSERV_FUNC(cmd_huggle
)
5876 /* CTCP must be via PRIVMSG, never notice */
5878 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
5880 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
5884 static CHANSERV_FUNC(cmd_calc
)
5886 char response
[MAXLEN
];
5889 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
5892 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5894 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5899 chanserv_adjust_limit(void *data
)
5901 struct mod_chanmode change
;
5902 struct chanData
*cData
= data
;
5903 struct chanNode
*channel
= cData
->channel
;
5906 if(IsSuspended(cData
))
5909 cData
->limitAdjusted
= now
;
5910 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
5911 if(cData
->modes
.modes_set
& MODE_LIMIT
)
5913 if(limit
> cData
->modes
.new_limit
)
5914 limit
= cData
->modes
.new_limit
;
5915 else if(limit
== cData
->modes
.new_limit
)
5919 mod_chanmode_init(&change
);
5920 change
.modes_set
= MODE_LIMIT
;
5921 change
.new_limit
= limit
;
5922 mod_chanmode_announce(chanserv
, channel
, &change
);
5926 handle_new_channel(struct chanNode
*channel
)
5928 struct chanData
*cData
;
5930 if(!(cData
= channel
->channel_info
))
5933 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
5934 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
5936 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
5937 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
5940 /* Welcome to my worst nightmare. Warning: Read (or modify)
5941 the code below at your own risk. */
5943 handle_join(struct modeNode
*mNode
)
5945 struct mod_chanmode change
;
5946 struct userNode
*user
= mNode
->user
;
5947 struct chanNode
*channel
= mNode
->channel
;
5948 struct chanData
*cData
;
5949 struct userData
*uData
= NULL
;
5950 struct banData
*bData
;
5951 struct handle_info
*handle
;
5952 unsigned int modes
= 0, info
= 0;
5955 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
5958 cData
= channel
->channel_info
;
5959 if(channel
->members
.used
> cData
->max
)
5960 cData
->max
= channel
->members
.used
;
5962 /* Check for bans. If they're joining through a ban, one of two
5964 * 1: Join during a netburst, by riding the break. Kick them
5965 * unless they have ops or voice in the channel.
5966 * 2: They're allowed to join through the ban (an invite in
5967 * ircu2.10, or a +e on Hybrid, or something).
5968 * If they're not joining through a ban, and the banlist is not
5969 * full, see if they're on the banlist for the channel. If so,
5972 /* This is really, really stupid. not all banned people are kicked.
5973 * sometimes we like to leave them unkicked.
5974 * I tried to explain this to the srvx developers and
5975 * got insulted.. hence one reason for this fork.
5977 if(user->uplink->burst && !mNode->modes)
5980 for(ii = 0; ii < channel->banlist.used; ii++)
5982 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5984 ** Riding a netburst. Naughty. **
5985 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5992 mod_chanmode_init(&change
);
5994 if(channel
->banlist
.used
< MAXBANS
)
5996 /* Not joining through a ban. */
5997 for(bData
= cData
->bans
;
5998 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
5999 bData
= bData
->next
);
6003 char kick_reason
[MAXLEN
];
6004 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6006 bData
->triggered
= now
;
6007 if(bData
!= cData
->bans
)
6009 /* Shuffle the ban to the head of the list. */
6011 bData
->next
->prev
= bData
->prev
;
6013 bData
->prev
->next
= bData
->next
;
6016 bData
->next
= cData
->bans
;
6019 cData
->bans
->prev
= bData
;
6020 cData
->bans
= bData
;
6023 change
.args
[0].mode
= MODE_BAN
;
6024 change
.args
[0].u
.hostmask
= bData
->mask
;
6025 mod_chanmode_announce(chanserv
, channel
, &change
);
6026 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6031 /* ChanServ will not modify the limits in join-flooded channels.
6032 It will also skip DynLimit processing when the user (or srvx)
6033 is bursting in, because there are likely more incoming. */
6034 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6035 && !user
->uplink
->burst
6036 && !channel
->join_flooded
6037 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6039 /* The user count has begun "bumping" into the channel limit,
6040 so set a timer to raise the limit a bit. Any previous
6041 timers are removed so three incoming users within the delay
6042 results in one limit change, not three. */
6044 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6045 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6048 if(channel
->join_flooded
)
6050 /* don't automatically give ops or voice during a join flood */
6052 else if(cData
->lvlOpts
[lvlGiveOps
] == 0)
6053 modes
|= MODE_CHANOP
;
6054 else if(cData
->lvlOpts
[lvlGiveHalfOps
] == 0)
6055 modes
|= MODE_HALFOP
;
6056 else if(cData
->lvlOpts
[lvlGiveVoice
] == 0)
6057 modes
|= MODE_VOICE
;
6059 greeting
= cData
->greeting
;
6060 if(user
->handle_info
)
6062 handle
= user
->handle_info
;
6064 if(IsHelper(user
) && !IsHelping(user
))
6067 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6069 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6071 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6077 uData
= GetTrueChannelAccess(cData
, handle
);
6078 if(uData
&& !IsUserSuspended(uData
))
6080 /* Ops and above were handled by the above case. */
6081 if(IsUserAutoOp(uData
))
6083 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
6084 modes
|= MODE_CHANOP
;
6085 if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
6086 modes
|= MODE_HALFOP
;
6087 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
6088 modes
|= MODE_VOICE
;
6090 if(uData
->access
>= UL_PRESENT
)
6091 cData
->visited
= now
;
6092 if(cData
->user_greeting
)
6093 greeting
= cData
->user_greeting
;
6095 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6096 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6103 if(!user
->uplink
->burst
)
6107 if(modes
& MODE_CHANOP
) {
6108 modes
&= ~MODE_HALFOP
;
6109 modes
&= ~MODE_VOICE
;
6111 change
.args
[0].mode
= modes
;
6112 change
.args
[0].u
.member
= mNode
;
6113 mod_chanmode_announce(chanserv
, channel
, &change
);
6115 if(greeting
&& !user
->uplink
->burst
)
6116 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6118 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6124 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6126 struct mod_chanmode change
;
6127 struct userData
*channel
;
6128 unsigned int ii
, jj
;
6130 if(!user
->handle_info
)
6133 mod_chanmode_init(&change
);
6135 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6137 struct chanNode
*cn
;
6138 struct modeNode
*mn
;
6139 if(IsUserSuspended(channel
)
6140 || IsSuspended(channel
->channel
)
6141 || !(cn
= channel
->channel
->channel
))
6144 mn
= GetUserMode(cn
, user
);
6147 if(!IsUserSuspended(channel
)
6148 && IsUserAutoInvite(channel
)
6149 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6151 && !user
->uplink
->burst
)
6152 irc_invite(chanserv
, user
, cn
);
6156 if(channel
->access
>= UL_PRESENT
)
6157 channel
->channel
->visited
= now
;
6159 if(IsUserAutoOp(channel
))
6161 if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveOps
])
6162 change
.args
[0].mode
= MODE_CHANOP
;
6163 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
6164 change
.args
[0].mode
= MODE_HALFOP
;
6165 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveVoice
])
6166 change
.args
[0].mode
= MODE_VOICE
;
6168 change
.args
[0].mode
= 0;
6169 change
.args
[0].u
.member
= mn
;
6170 if(change
.args
[0].mode
)
6171 mod_chanmode_announce(chanserv
, cn
, &change
);
6174 channel
->seen
= now
;
6175 channel
->present
= 1;
6178 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6180 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6181 struct banData
*ban
;
6183 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6184 || !channel
->channel_info
6185 || IsSuspended(channel
->channel_info
))
6187 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6188 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6190 if(jj
< channel
->banlist
.used
)
6192 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6194 char kick_reason
[MAXLEN
];
6195 if(!user_matches_glob(user
, ban
->mask
, 1))
6197 change
.args
[0].mode
= MODE_BAN
;
6198 change
.args
[0].u
.hostmask
= ban
->mask
;
6199 mod_chanmode_announce(chanserv
, channel
, &change
);
6200 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6201 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6202 ban
->triggered
= now
;
6207 if(IsSupportHelper(user
))
6209 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6211 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6213 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6221 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6223 struct chanData
*cData
;
6224 struct userData
*uData
;
6226 cData
= mn
->channel
->channel_info
;
6227 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6230 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6232 /* Allow for a bit of padding so that the limit doesn't
6233 track the user count exactly, which could get annoying. */
6234 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6236 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6237 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6241 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6243 scan_user_presence(uData
, mn
->user
);
6247 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6249 unsigned int ii
, jj
;
6250 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6252 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6253 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6255 if(jj
< mn
->user
->channels
.used
)
6258 if(ii
== chanserv_conf
.support_channels
.used
)
6259 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6264 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6266 struct userData
*uData
;
6268 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6269 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6270 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6273 if(protect_user(victim
, kicker
, channel
->channel_info
))
6275 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6276 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6279 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6284 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6286 struct chanData
*cData
;
6288 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6291 cData
= channel
->channel_info
;
6292 if(bad_topic(channel
, user
, channel
->topic
))
6294 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6295 if(cData
->topic_mask
&& match_ircglob(old_topic
, cData
->topic_mask
))
6296 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6297 else if(cData
->topic
)
6298 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
6301 /* With topicsnarf, grab the topic and save it as the default topic. */
6302 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6305 cData
->topic
= strdup(channel
->topic
);
6311 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6313 struct mod_chanmode
*bounce
= NULL
;
6314 unsigned int bnc
, ii
;
6317 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6320 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6321 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6323 char correct
[MAXLEN
];
6324 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6325 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6326 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6328 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6330 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6332 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6333 if(!protect_user(victim
, user
, channel
->channel_info
))
6336 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6339 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6340 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6341 if(bounce
->args
[bnc
].u
.member
)
6345 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6346 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6348 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6350 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6352 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6353 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6356 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6357 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6358 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6361 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6363 const char *ban
= change
->args
[ii
].u
.hostmask
;
6364 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6367 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6368 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6369 bounce
->args
[bnc
].u
.hostmask
= ban
;
6371 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6376 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6377 mod_chanmode_announce(chanserv
, channel
, bounce
);
6378 mod_chanmode_free(bounce
);
6383 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6385 struct chanNode
*channel
;
6386 struct banData
*bData
;
6387 struct mod_chanmode change
;
6388 unsigned int ii
, jj
;
6389 char kick_reason
[MAXLEN
];
6391 mod_chanmode_init(&change
);
6393 change
.args
[0].mode
= MODE_BAN
;
6394 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6396 channel
= user
->channels
.list
[ii
]->channel
;
6397 /* Need not check for bans if they're opped or voiced. */
6398 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6400 /* Need not check for bans unless channel registration is active. */
6401 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6403 /* Look for a matching ban already on the channel. */
6404 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6405 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6407 /* Need not act if we found one. */
6408 if(jj
< channel
->banlist
.used
)
6410 /* Look for a matching ban in this channel. */
6411 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6413 if(!user_matches_glob(user
, bData
->mask
, 1))
6415 change
.args
[0].u
.hostmask
= bData
->mask
;
6416 mod_chanmode_announce(chanserv
, channel
, &change
);
6417 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6418 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6419 bData
->triggered
= now
;
6420 break; /* we don't need to check any more bans in the channel */
6425 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6427 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6431 dict_remove2(handle_dnrs
, old_handle
, 1);
6432 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6433 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6438 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6440 struct userNode
*h_user
;
6442 if(handle
->channels
)
6444 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6445 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6447 while(handle
->channels
)
6448 del_channel_user(handle
->channels
, 1);
6453 handle_server_link(UNUSED_ARG(struct server
*server
))
6455 struct chanData
*cData
;
6457 for(cData
= channelList
; cData
; cData
= cData
->next
)
6459 if(!IsSuspended(cData
))
6460 cData
->may_opchan
= 1;
6461 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6462 && !cData
->channel
->join_flooded
6463 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6464 < chanserv_conf
.adjust_threshold
))
6466 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6467 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6473 chanserv_conf_read(void)
6477 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6478 struct mod_chanmode
*change
;
6479 struct string_list
*strlist
;
6480 struct chanNode
*chan
;
6483 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6485 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6488 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6489 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6490 chanserv_conf
.support_channels
.used
= 0;
6491 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6493 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6495 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6498 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6500 channelList_append(&chanserv_conf
.support_channels
, chan
);
6503 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6506 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6509 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6511 channelList_append(&chanserv_conf
.support_channels
, chan
);
6513 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6514 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6515 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6516 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6517 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6518 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6519 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6520 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6521 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6522 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6523 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6524 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6525 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6526 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6527 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6528 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6529 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6530 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6531 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6532 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6533 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6534 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6535 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6537 NickChange(chanserv
, str
, 0);
6538 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6539 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6540 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6541 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6542 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6543 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6544 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6545 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6546 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6547 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6548 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6549 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6550 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6551 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6552 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6553 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6554 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6557 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6558 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6559 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6561 chanserv_conf
.default_modes
= *change
;
6562 mod_chanmode_free(change
);
6564 free_string_list(chanserv_conf
.set_shows
);
6565 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6567 strlist
= string_list_copy(strlist
);
6570 static const char *list
[] = {
6571 /* free form text */
6572 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6573 /* options based on user level */
6574 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveHalfOps", "GiveOps", "EnfOps",
6575 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6576 /* multiple choice options */
6577 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6578 /* binary options */
6579 "DynLimit", "NoDelete",
6584 strlist
= alloc_string_list(ArrayLength(list
)-1);
6585 for(ii
=0; list
[ii
]; ii
++)
6586 string_list_append(strlist
, strdup(list
[ii
]));
6588 chanserv_conf
.set_shows
= strlist
;
6589 /* We don't look things up now, in case the list refers to options
6590 * defined by modules initialized after this point. Just mark the
6591 * function list as invalid, so it will be initialized.
6593 set_shows_list
.used
= 0;
6594 free_string_list(chanserv_conf
.eightball
);
6595 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6598 strlist
= string_list_copy(strlist
);
6602 strlist
= alloc_string_list(4);
6603 string_list_append(strlist
, strdup("Yes."));
6604 string_list_append(strlist
, strdup("No."));
6605 string_list_append(strlist
, strdup("Maybe so."));
6607 chanserv_conf
.eightball
= strlist
;
6608 free_string_list(chanserv_conf
.old_ban_names
);
6609 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6611 strlist
= string_list_copy(strlist
);
6613 strlist
= alloc_string_list(2);
6614 chanserv_conf
.old_ban_names
= strlist
;
6615 /* the variable itself is actually declared in proto-common.c; this is equally
6617 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6618 off_channel
= str
? atoi(str
) : 0;
6622 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6625 struct note_type
*ntype
;
6628 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6630 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6633 if(!(ntype
= chanserv_create_note_type(key
)))
6635 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6639 /* Figure out set access */
6640 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6642 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6643 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6645 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6647 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6648 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6650 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6652 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6656 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6657 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6658 ntype
->set_access
.min_opserv
= 0;
6661 /* Figure out visibility */
6662 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6663 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6664 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6665 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6666 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6667 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6668 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6669 ntype
->visible_type
= NOTE_VIS_ALL
;
6671 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6673 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6674 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6678 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6680 struct handle_info
*handle
;
6681 struct userData
*uData
;
6682 char *seen
, *inf
, *flags
;
6684 unsigned short access
;
6686 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6688 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6692 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6693 if(access
> UL_OWNER
)
6695 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6699 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
6700 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
6701 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
6702 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
6703 handle
= get_handle_info(key
);
6706 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
6710 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
6711 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
6715 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6717 struct banData
*bData
;
6718 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
6719 time_t set_time
, triggered_time
, expires_time
;
6721 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6723 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
6727 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
6728 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
6729 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
6730 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
6731 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
6732 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
6734 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
6735 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
6737 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
6739 expires_time
= set_time
+ atoi(s_duration
);
6743 if(expires_time
&& (expires_time
< now
))
6746 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
6749 static struct suspended
*
6750 chanserv_read_suspended(dict_t obj
)
6752 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
6756 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
6757 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6758 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
6759 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6760 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
6761 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6762 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
6763 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
6764 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
6765 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
6770 chanserv_channel_read(const char *key
, struct record_data
*hir
)
6772 struct suspended
*suspended
;
6773 struct mod_chanmode
*modes
;
6774 struct chanNode
*cNode
;
6775 struct chanData
*cData
;
6776 struct dict
*channel
, *obj
;
6777 char *str
, *argv
[10];
6781 channel
= hir
->d
.object
;
6783 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
6786 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
6789 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
6792 cData
= register_channel(cNode
, str
);
6795 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
6799 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
6801 enum levelOption lvlOpt
;
6802 enum charOption chOpt
;
6804 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
6805 cData
->flags
= atoi(str
);
6807 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6809 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
6811 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
6812 else if(levelOptions
[lvlOpt
].old_flag
)
6814 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6815 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
6817 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6821 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6823 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
6825 cData
->chOpts
[chOpt
] = str
[0];
6828 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
6830 enum levelOption lvlOpt
;
6831 enum charOption chOpt
;
6834 cData
->flags
= base64toint(str
, 5);
6835 count
= strlen(str
+= 5);
6836 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6839 if(levelOptions
[lvlOpt
].old_flag
)
6841 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6842 lvl
= levelOptions
[lvlOpt
].flag_value
;
6844 lvl
= levelOptions
[lvlOpt
].default_value
;
6846 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
6848 case 'c': lvl
= UL_COOWNER
; break;
6849 case 'm': lvl
= UL_MANAGER
; break;
6850 case 'n': lvl
= UL_OWNER
+1; break;
6851 case 'o': lvl
= UL_OP
; break;
6852 case 'p': lvl
= UL_PEON
; break;
6853 case 'h': lvl
= UL_HALFOP
; break;
6854 case 'w': lvl
= UL_OWNER
; break;
6855 default: lvl
= 0; break;
6857 cData
->lvlOpts
[lvlOpt
] = lvl
;
6859 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6860 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
6863 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
6865 suspended
= chanserv_read_suspended(obj
);
6866 cData
->suspended
= suspended
;
6867 suspended
->cData
= cData
;
6868 /* We could use suspended->expires and suspended->revoked to
6869 * set the CHANNEL_SUSPENDED flag, but we don't. */
6871 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
6873 suspended
= calloc(1, sizeof(*suspended
));
6874 suspended
->issued
= 0;
6875 suspended
->revoked
= 0;
6876 suspended
->suspender
= strdup(str
);
6877 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
6878 suspended
->expires
= str
? atoi(str
) : 0;
6879 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
6880 suspended
->reason
= strdup(str
? str
: "No reason");
6881 suspended
->previous
= NULL
;
6882 cData
->suspended
= suspended
;
6883 suspended
->cData
= cData
;
6887 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6888 suspended
= NULL
; /* to squelch a warning */
6891 if(IsSuspended(cData
)) {
6892 if(suspended
->expires
> now
)
6893 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6894 else if(suspended
->expires
)
6895 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6898 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
6899 struct mod_chanmode change
;
6900 mod_chanmode_init(&change
);
6902 change
.args
[0].mode
= MODE_CHANOP
;
6903 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
6904 mod_chanmode_announce(chanserv
, cNode
, &change
);
6907 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
6908 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6909 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
6910 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6911 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
6912 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6913 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
6914 cData
->max
= str
? atoi(str
) : 0;
6915 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
6916 cData
->greeting
= str
? strdup(str
) : NULL
;
6917 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
6918 cData
->user_greeting
= str
? strdup(str
) : NULL
;
6919 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
6920 cData
->topic_mask
= str
? strdup(str
) : NULL
;
6921 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
6922 cData
->topic
= str
? strdup(str
) : NULL
;
6924 if(!IsSuspended(cData
)
6925 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
6926 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
6927 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
6928 cData
->modes
= *modes
;
6930 cData
->modes
.modes_set
|= MODE_REGISTERED
;
6931 if(cData
->modes
.argc
> 1)
6932 cData
->modes
.argc
= 1;
6933 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
6934 mod_chanmode_free(modes
);
6937 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
6938 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6939 user_read_helper(iter_key(it
), iter_data(it
), cData
);
6941 if(!cData
->users
&& !IsProtected(cData
))
6943 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
6944 unregister_channel(cData
, "has empty user list.");
6948 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
6949 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6950 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
6952 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
6953 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6955 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
6956 struct record_data
*rd
= iter_data(it
);
6957 const char *note
, *setter
;
6959 if(rd
->type
!= RECDB_OBJECT
)
6961 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
6965 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
6967 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
6969 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
6973 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
6974 if(!setter
) setter
= "<unknown>";
6975 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
6983 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
6985 const char *setter
, *reason
, *str
;
6986 struct do_not_register
*dnr
;
6988 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
6991 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
6994 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
6997 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7000 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7003 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7005 dnr
->set
= atoi(str
);
7011 chanserv_saxdb_read(struct dict
*database
)
7013 struct dict
*section
;
7016 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7017 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7018 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7020 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7021 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7022 chanserv_channel_read(iter_key(it
), iter_data(it
));
7024 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7025 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7026 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7032 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7034 int high_present
= 0;
7035 saxdb_start_record(ctx
, KEY_USERS
, 1);
7036 for(; uData
; uData
= uData
->next
)
7038 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7040 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7041 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7042 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7044 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7046 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7047 saxdb_end_record(ctx
);
7049 saxdb_end_record(ctx
);
7050 return high_present
;
7054 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7058 saxdb_start_record(ctx
, KEY_BANS
, 1);
7059 for(; bData
; bData
= bData
->next
)
7061 saxdb_start_record(ctx
, bData
->mask
, 0);
7062 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7063 if(bData
->triggered
)
7064 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7066 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7068 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7070 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7071 saxdb_end_record(ctx
);
7073 saxdb_end_record(ctx
);
7077 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7079 saxdb_start_record(ctx
, name
, 0);
7080 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7081 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7083 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7085 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7087 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7089 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7090 saxdb_end_record(ctx
);
7094 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7098 enum levelOption lvlOpt
;
7099 enum charOption chOpt
;
7101 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7103 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7104 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7106 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7107 if(channel
->registrar
)
7108 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7109 if(channel
->greeting
)
7110 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7111 if(channel
->user_greeting
)
7112 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7113 if(channel
->topic_mask
)
7114 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7115 if(channel
->suspended
)
7116 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7118 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7119 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7120 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7121 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7122 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7124 buf
[0] = channel
->chOpts
[chOpt
];
7126 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7128 saxdb_end_record(ctx
);
7130 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7132 mod_chanmode_format(&channel
->modes
, buf
);
7133 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7136 high_present
= chanserv_write_users(ctx
, channel
->users
);
7137 chanserv_write_bans(ctx
, channel
->bans
);
7139 if(dict_size(channel
->notes
))
7143 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7144 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7146 struct note
*note
= iter_data(it
);
7147 saxdb_start_record(ctx
, iter_key(it
), 0);
7148 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7149 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7150 saxdb_end_record(ctx
);
7152 saxdb_end_record(ctx
);
7155 if(channel
->ownerTransfer
)
7156 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7157 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7158 saxdb_end_record(ctx
);
7162 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7166 saxdb_start_record(ctx
, ntype
->name
, 0);
7167 switch(ntype
->set_access_type
)
7169 case NOTE_SET_CHANNEL_ACCESS
:
7170 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7172 case NOTE_SET_CHANNEL_SETTER
:
7173 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7175 case NOTE_SET_PRIVILEGED
: default:
7176 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7179 switch(ntype
->visible_type
)
7181 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7182 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7183 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7185 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7186 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7187 saxdb_end_record(ctx
);
7191 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7193 struct do_not_register
*dnr
;
7196 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7198 dnr
= iter_data(it
);
7199 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7201 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7202 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7203 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7204 saxdb_end_record(ctx
);
7209 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7212 struct chanData
*channel
;
7215 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7216 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7217 chanserv_write_note_type(ctx
, iter_data(it
));
7218 saxdb_end_record(ctx
);
7221 saxdb_start_record(ctx
, KEY_DNR
, 1);
7222 write_dnrs_helper(ctx
, handle_dnrs
);
7223 write_dnrs_helper(ctx
, plain_dnrs
);
7224 write_dnrs_helper(ctx
, mask_dnrs
);
7225 saxdb_end_record(ctx
);
7228 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7229 for(channel
= channelList
; channel
; channel
= channel
->next
)
7230 chanserv_write_channel(ctx
, channel
);
7231 saxdb_end_record(ctx
);
7237 chanserv_db_cleanup(void) {
7239 unreg_part_func(handle_part
);
7241 unregister_channel(channelList
, "terminating.");
7242 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7243 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7244 free(chanserv_conf
.support_channels
.list
);
7245 dict_delete(handle_dnrs
);
7246 dict_delete(plain_dnrs
);
7247 dict_delete(mask_dnrs
);
7248 dict_delete(note_types
);
7249 free_string_list(chanserv_conf
.eightball
);
7250 free_string_list(chanserv_conf
.old_ban_names
);
7251 free_string_list(chanserv_conf
.set_shows
);
7252 free(set_shows_list
.list
);
7253 free(uset_shows_list
.list
);
7256 struct userData
*helper
= helperList
;
7257 helperList
= helperList
->next
;
7262 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7263 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7264 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7267 init_chanserv(const char *nick
)
7269 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7270 conf_register_reload(chanserv_conf_read
);
7272 reg_server_link_func(handle_server_link
);
7274 reg_new_channel_func(handle_new_channel
);
7275 reg_join_func(handle_join
);
7276 reg_part_func(handle_part
);
7277 reg_kick_func(handle_kick
);
7278 reg_topic_func(handle_topic
);
7279 reg_mode_change_func(handle_mode
);
7280 reg_nick_change_func(handle_nick_change
);
7282 reg_auth_func(handle_auth
);
7283 reg_handle_rename_func(handle_rename
);
7284 reg_unreg_func(handle_unreg
);
7286 handle_dnrs
= dict_new();
7287 dict_set_free_data(handle_dnrs
, free
);
7288 plain_dnrs
= dict_new();
7289 dict_set_free_data(plain_dnrs
, free
);
7290 mask_dnrs
= dict_new();
7291 dict_set_free_data(mask_dnrs
, free
);
7293 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7294 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7295 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7296 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7297 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7298 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7299 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7300 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7301 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7302 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7304 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7305 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7307 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7308 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7309 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7310 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7311 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7313 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7314 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7315 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7316 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7317 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7318 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7320 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7321 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7322 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7323 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7325 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7326 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7327 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7328 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7329 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7330 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7331 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7332 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7333 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7334 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7336 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7337 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7338 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7339 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7340 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7341 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7342 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7343 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7344 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7345 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7346 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7347 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7348 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7349 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7351 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7352 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7353 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7354 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7355 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7357 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7358 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7360 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7361 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7362 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7363 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7364 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7365 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7366 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7367 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7368 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7369 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7370 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7371 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7373 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7374 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7376 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7377 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7378 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7379 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7381 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7382 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7383 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7384 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7385 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7387 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7388 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7389 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7390 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7391 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7392 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7393 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7395 /* Channel options */
7396 DEFINE_CHANNEL_OPTION(defaulttopic
);
7397 DEFINE_CHANNEL_OPTION(topicmask
);
7398 DEFINE_CHANNEL_OPTION(greeting
);
7399 DEFINE_CHANNEL_OPTION(usergreeting
);
7400 DEFINE_CHANNEL_OPTION(modes
);
7401 DEFINE_CHANNEL_OPTION(enfops
);
7402 DEFINE_CHANNEL_OPTION(enfhalfops
);
7403 DEFINE_CHANNEL_OPTION(giveops
);
7404 DEFINE_CHANNEL_OPTION(givehalfops
);
7405 DEFINE_CHANNEL_OPTION(protect
);
7406 DEFINE_CHANNEL_OPTION(enfmodes
);
7407 DEFINE_CHANNEL_OPTION(enftopic
);
7408 DEFINE_CHANNEL_OPTION(pubcmd
);
7409 DEFINE_CHANNEL_OPTION(givevoice
);
7410 DEFINE_CHANNEL_OPTION(userinfo
);
7411 DEFINE_CHANNEL_OPTION(dynlimit
);
7412 DEFINE_CHANNEL_OPTION(topicsnarf
);
7413 DEFINE_CHANNEL_OPTION(nodelete
);
7414 DEFINE_CHANNEL_OPTION(toys
);
7415 DEFINE_CHANNEL_OPTION(setters
);
7416 DEFINE_CHANNEL_OPTION(topicrefresh
);
7417 DEFINE_CHANNEL_OPTION(ctcpusers
);
7418 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7419 DEFINE_CHANNEL_OPTION(inviteme
);
7421 DEFINE_CHANNEL_OPTION(offchannel
);
7422 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7424 /* Alias set topic to set defaulttopic for compatibility. */
7425 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7428 DEFINE_USER_OPTION(noautoop
);
7429 DEFINE_USER_OPTION(autoinvite
);
7430 DEFINE_USER_OPTION(info
);
7432 /* Alias uset autovoice to uset autoop. */
7433 modcmd_register(chanserv_module
, "uset noautovoice", user_opt_noautoop
, 1, 0, NULL
);
7435 note_types
= dict_new();
7436 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7439 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7440 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7441 service_register(chanserv
)->trigger
= '!';
7442 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7444 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7446 if(chanserv_conf
.channel_expire_frequency
)
7447 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7449 if(chanserv_conf
.refresh_period
)
7451 time_t next_refresh
;
7452 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7453 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7456 reg_exit_func(chanserv_db_cleanup
);
7457 message_register_table(msgtab
);