1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
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", "$bDo-Not-Register Channels$b" },
135 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago 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 %s (%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%s$b, not %s." },
193 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
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 is at most %d characters and matches the topic mask pattern." },
232 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
233 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
234 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
236 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
237 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
238 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
239 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
240 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
241 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
242 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
243 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
244 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
245 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above 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", "$b%s Users From Level %s To %s$b" },
323 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
324 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
325 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
326 { "CSMSG_BANS_HEADER", "$bBans in %s$b" },
328 /* Channel note list */
329 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
330 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
331 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
332 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
333 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
334 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
335 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
336 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
337 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
338 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
339 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
340 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
341 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
342 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
343 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
345 /* Channel [un]suspension */
346 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
347 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
348 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
349 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
350 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
351 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
352 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
354 /* Access information */
355 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
356 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
357 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
358 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
359 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
360 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
361 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
362 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
363 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
364 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
365 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
367 /* Seen information */
368 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
369 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
370 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
371 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
373 /* Names information */
374 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
375 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
377 /* Channel information */
378 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
379 { "CSMSG_BAR", "----------------------------------------"},
380 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
381 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
382 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
383 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
384 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
385 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
386 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
387 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
388 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
389 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
390 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
398 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
399 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
400 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
402 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
403 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
404 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
405 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
406 { "CSMSG_PEEK_OPS", "$bOps:$b" },
407 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
408 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
410 /* Network information */
411 { "CSMSG_NETWORK_INFO", "Network Information:" },
412 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
413 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
414 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
415 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
416 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
417 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
418 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
419 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
422 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
423 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
424 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
426 /* Channel searches */
427 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
428 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
429 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
430 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
432 /* Channel configuration */
433 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
434 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
435 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
436 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
439 { "CSMSG_USER_OPTIONS", "User Options:" },
440 { "CSMSG_USER_PROTECTED", "That user is protected." },
443 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
444 { "CSMSG_PING_RESPONSE", "Pong!" },
445 { "CSMSG_WUT_RESPONSE", "wut" },
446 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
447 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
448 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
449 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
450 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
451 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
452 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
455 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
459 /* eject_user and unban_user flags */
460 #define ACTION_KICK 0x0001
461 #define ACTION_BAN 0x0002
462 #define ACTION_ADD_BAN 0x0004
463 #define ACTION_ADD_TIMED_BAN 0x0008
464 #define ACTION_UNBAN 0x0010
465 #define ACTION_DEL_BAN 0x0020
467 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
468 #define MODELEN 40 + KEYLEN
472 #define CSFUNC_ARGS user, channel, argc, argv, cmd
474 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
475 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
476 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
477 reply("MSG_MISSING_PARAMS", argv[0]); \
481 DECLARE_LIST(dnrList
, struct do_not_register
*);
482 DEFINE_LIST(dnrList
, struct do_not_register
*);
484 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
486 struct userNode
*chanserv
;
489 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
490 static struct log_type
*CS_LOG
;
494 struct channelList support_channels
;
495 struct mod_chanmode default_modes
;
497 unsigned long db_backup_frequency
;
498 unsigned long channel_expire_frequency
;
501 unsigned int adjust_delay
;
502 long channel_expire_delay
;
503 unsigned int nodelete_level
;
505 unsigned int adjust_threshold
;
506 int join_flood_threshold
;
508 unsigned int greeting_length
;
509 unsigned int refresh_period
;
510 unsigned int giveownership_period
;
512 unsigned int max_owned
;
513 unsigned int max_chan_users
;
514 unsigned int max_chan_bans
;
515 unsigned int max_userinfo_length
;
517 struct string_list
*set_shows
;
518 struct string_list
*eightball
;
519 struct string_list
*old_ban_names
;
521 const char *ctcp_short_ban_duration
;
522 const char *ctcp_long_ban_duration
;
524 const char *irc_operator_epithet
;
525 const char *network_helper_epithet
;
526 const char *support_helper_epithet
;
531 struct userNode
*user
;
532 struct userNode
*bot
;
533 struct chanNode
*channel
;
535 unsigned short lowest
;
536 unsigned short highest
;
537 struct userData
**users
;
538 struct helpfile_table table
;
541 enum note_access_type
543 NOTE_SET_CHANNEL_ACCESS
,
544 NOTE_SET_CHANNEL_SETTER
,
548 enum note_visible_type
551 NOTE_VIS_CHANNEL_USERS
,
557 enum note_access_type set_access_type
;
559 unsigned int min_opserv
;
560 unsigned short min_ulevel
;
562 enum note_visible_type visible_type
;
563 unsigned int max_length
;
570 struct note_type
*type
;
571 char setter
[NICKSERV_HANDLE_LEN
+1];
575 static unsigned int registered_channels
;
576 static unsigned int banCount
;
578 static const struct {
581 unsigned short level
;
583 } accessLevels
[] = { /* MUST be orderd less to most! */
584 { "peon", "Peon", UL_PEON
, '+' },
585 { "halfop", "HalfOp", UL_HALFOP
, '%' },
586 { "op", "Op", UL_OP
, '@' },
587 { "manager", "Manager", UL_MANAGER
, '%' },
588 { "coowner", "Coowner", UL_COOWNER
, '*' },
589 { "owner", "Owner", UL_OWNER
, '!' },
590 { "helper", "BUG:", UL_HELPER
, 'X' }
593 static const struct {
596 unsigned short default_value
;
597 unsigned int old_idx
;
598 unsigned int old_flag
;
599 unsigned short flag_value
;
601 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
602 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
603 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
604 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
605 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
606 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
607 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
608 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
609 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
610 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
611 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
612 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
613 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
616 struct charOptionValues
{
619 } protectValues
[] = {
620 { 'a', "CSMSG_PROTECT_ALL" },
621 { 'e', "CSMSG_PROTECT_EQUAL" },
622 { 'l', "CSMSG_PROTECT_LOWER" },
623 { 'n', "CSMSG_PROTECT_NONE" }
625 { 'd', "CSMSG_TOYS_DISABLED" },
626 { 'n', "CSMSG_TOYS_PRIVATE" },
627 { 'p', "CSMSG_TOYS_PUBLIC" }
628 }, topicRefreshValues
[] = {
629 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
630 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
631 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
632 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
633 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
634 }, ctcpReactionValues
[] = {
635 { 'k', "CSMSG_CTCPREACTION_KICK" },
636 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
637 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
638 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
641 static const struct {
645 unsigned int old_idx
;
647 struct charOptionValues
*values
;
649 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
650 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
651 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
652 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
655 struct userData
*helperList
;
656 struct chanData
*channelList
;
657 static struct module *chanserv_module
;
658 static unsigned int userCount
;
660 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
661 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
662 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
665 user_level_from_name(const char *name
, unsigned short clamp_level
)
667 unsigned int level
= 0, ii
;
669 level
= strtoul(name
, NULL
, 10);
670 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
671 if(!irccasecmp(name
, accessLevels
[ii
].name
))
672 level
= accessLevels
[ii
].level
;
673 if(level
> clamp_level
)
679 user_level_name_from_level(int level
)
687 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
688 if(level
>= accessLevels
[ii
].level
)
689 highest
= accessLevels
[ii
].title
;
695 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
698 *minl
= strtoul(arg
, &sep
, 10);
706 *maxl
= strtoul(sep
+1, &sep
, 10);
714 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
716 struct userData
*uData
, **head
;
718 if(!channel
|| !handle
)
721 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
722 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
724 for(uData
= helperList
;
725 uData
&& uData
->handle
!= handle
;
726 uData
= uData
->next
);
730 uData
= calloc(1, sizeof(struct userData
));
731 uData
->handle
= handle
;
733 uData
->access
= UL_HELPER
;
739 uData
->next
= helperList
;
741 helperList
->prev
= uData
;
749 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
750 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
753 head
= &(channel
->users
);
756 if(uData
&& (uData
!= *head
))
758 /* Shuffle the user to the head of whatever list he was in. */
760 uData
->next
->prev
= uData
->prev
;
762 uData
->prev
->next
= uData
->next
;
768 (**head
).prev
= uData
;
775 /* Returns non-zero if user has at least the minimum access.
776 * exempt_owner is set when handling !set, so the owner can set things
779 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
781 struct userData
*uData
;
782 struct chanData
*cData
= channel
->channel_info
;
783 unsigned short minimum
= cData
->lvlOpts
[opt
];
786 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
789 if(minimum
<= uData
->access
)
791 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
796 /* Scan for other users authenticated to the same handle
797 still in the channel. If so, keep them listed as present.
799 user is optional, if not null, it skips checking that userNode
800 (for the handle_part function) */
802 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
806 if(IsSuspended(uData
->channel
)
807 || IsUserSuspended(uData
)
808 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
820 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
822 unsigned int eflags
, argc
;
824 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
826 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
827 if(!channel
->channel_info
828 || IsSuspended(channel
->channel_info
)
830 || !ircncasecmp(text
, "ACTION ", 7))
832 /* Figure out the minimum level needed to CTCP the channel */
833 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
835 /* We need to enforce against them; do so. */
838 argv
[1] = user
->nick
;
840 if(GetUserMode(channel
, user
))
841 eflags
|= ACTION_KICK
;
842 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
843 default: case 'k': /* just do the kick */ break;
845 eflags
|= ACTION_BAN
;
848 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
849 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
852 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
853 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
856 argv
[argc
++] = bad_ctcp_reason
;
857 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
861 chanserv_create_note_type(const char *name
)
863 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
864 strcpy(ntype
->name
, name
);
866 dict_insert(note_types
, ntype
->name
, ntype
);
871 chanserv_deref_note_type(void *data
)
873 struct note_type
*ntype
= data
;
875 if(--ntype
->refs
> 0)
881 chanserv_flush_note_type(struct note_type
*ntype
)
883 struct chanData
*cData
;
884 for(cData
= channelList
; cData
; cData
= cData
->next
)
885 dict_remove(cData
->notes
, ntype
->name
);
889 chanserv_truncate_notes(struct note_type
*ntype
)
891 struct chanData
*cData
;
893 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
895 for(cData
= channelList
; cData
; cData
= cData
->next
) {
896 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
899 if(strlen(note
->note
) <= ntype
->max_length
)
901 dict_remove2(cData
->notes
, ntype
->name
, 1);
902 note
= realloc(note
, size
);
903 note
->note
[ntype
->max_length
] = 0;
904 dict_insert(cData
->notes
, ntype
->name
, note
);
908 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
911 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
914 unsigned int len
= strlen(text
);
916 if(len
> type
->max_length
) len
= type
->max_length
;
917 note
= calloc(1, sizeof(*note
) + len
);
919 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
920 memcpy(note
->note
, text
, len
);
922 dict_insert(channel
->notes
, type
->name
, note
);
928 chanserv_free_note(void *data
)
930 struct note
*note
= data
;
932 chanserv_deref_note_type(note
->type
);
933 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
937 static MODCMD_FUNC(cmd_createnote
) {
938 struct note_type
*ntype
;
939 unsigned int arg
= 1, existed
= 0, max_length
;
941 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
944 ntype
= chanserv_create_note_type(argv
[arg
]);
945 if(!irccasecmp(argv
[++arg
], "privileged"))
948 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
949 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
951 else if(!irccasecmp(argv
[arg
], "channel"))
953 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
956 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
959 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
960 ntype
->set_access
.min_ulevel
= ulvl
;
962 else if(!irccasecmp(argv
[arg
], "setter"))
964 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
968 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
972 if(!irccasecmp(argv
[++arg
], "privileged"))
973 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
974 else if(!irccasecmp(argv
[arg
], "channel_users"))
975 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
976 else if(!irccasecmp(argv
[arg
], "all"))
977 ntype
->visible_type
= NOTE_VIS_ALL
;
979 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
983 if((arg
+1) >= argc
) {
984 reply("MSG_MISSING_PARAMS", argv
[0]);
987 max_length
= strtoul(argv
[++arg
], NULL
, 0);
988 if(max_length
< 20 || max_length
> 450)
990 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
993 if(existed
&& (max_length
< ntype
->max_length
))
995 ntype
->max_length
= max_length
;
996 chanserv_truncate_notes(ntype
);
998 ntype
->max_length
= max_length
;
1001 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1003 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1008 dict_remove(note_types
, ntype
->name
);
1012 static MODCMD_FUNC(cmd_removenote
) {
1013 struct note_type
*ntype
;
1016 ntype
= dict_find(note_types
, argv
[1], NULL
);
1017 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1020 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1027 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1030 chanserv_flush_note_type(ntype
);
1032 dict_remove(note_types
, argv
[1]);
1033 reply("CSMSG_NOTE_DELETED", argv
[1]);
1038 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1042 if(orig
->modes_set
& change
->modes_clear
)
1044 if(orig
->modes_clear
& change
->modes_set
)
1046 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1047 && strcmp(orig
->new_key
, change
->new_key
))
1049 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1050 && (orig
->new_limit
!= change
->new_limit
))
1055 static char max_length_text
[MAXLEN
+1][16];
1057 static struct helpfile_expansion
1058 chanserv_expand_variable(const char *variable
)
1060 struct helpfile_expansion exp
;
1062 if(!irccasecmp(variable
, "notes"))
1065 exp
.type
= HF_TABLE
;
1066 exp
.value
.table
.length
= 1;
1067 exp
.value
.table
.width
= 3;
1068 exp
.value
.table
.flags
= 0;
1069 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1070 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1071 exp
.value
.table
.contents
[0][0] = "Note Type";
1072 exp
.value
.table
.contents
[0][1] = "Visibility";
1073 exp
.value
.table
.contents
[0][2] = "Max Length";
1074 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1076 struct note_type
*ntype
= iter_data(it
);
1079 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1080 row
= exp
.value
.table
.length
++;
1081 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1082 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1083 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1084 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1086 if(!max_length_text
[ntype
->max_length
][0])
1087 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1088 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1093 exp
.type
= HF_STRING
;
1094 exp
.value
.str
= NULL
;
1098 static struct chanData
*
1099 register_channel(struct chanNode
*cNode
, char *registrar
)
1101 struct chanData
*channel
;
1102 enum levelOption lvlOpt
;
1103 enum charOption chOpt
;
1105 channel
= calloc(1, sizeof(struct chanData
));
1107 channel
->notes
= dict_new();
1108 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1110 channel
->registrar
= strdup(registrar
);
1111 channel
->registered
= now
;
1112 channel
->visited
= now
;
1113 channel
->limitAdjusted
= now
;
1114 channel
->ownerTransfer
= now
;
1115 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1116 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1117 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1118 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1119 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1121 channel
->prev
= NULL
;
1122 channel
->next
= channelList
;
1125 channelList
->prev
= channel
;
1126 channelList
= channel
;
1127 registered_channels
++;
1129 channel
->channel
= cNode
;
1131 cNode
->channel_info
= channel
;
1136 static struct userData
*
1137 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1139 struct userData
*ud
;
1141 if(access
> UL_OWNER
)
1144 ud
= calloc(1, sizeof(*ud
));
1145 ud
->channel
= channel
;
1146 ud
->handle
= handle
;
1148 ud
->access
= access
;
1149 ud
->info
= info
? strdup(info
) : NULL
;
1152 ud
->next
= channel
->users
;
1154 channel
->users
->prev
= ud
;
1155 channel
->users
= ud
;
1157 channel
->userCount
++;
1161 ud
->u_next
= ud
->handle
->channels
;
1163 ud
->u_next
->u_prev
= ud
;
1164 ud
->handle
->channels
= ud
;
1169 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1172 del_channel_user(struct userData
*user
, int do_gc
)
1174 struct chanData
*channel
= user
->channel
;
1176 channel
->userCount
--;
1180 user
->prev
->next
= user
->next
;
1182 channel
->users
= user
->next
;
1184 user
->next
->prev
= user
->prev
;
1187 user
->u_prev
->u_next
= user
->u_next
;
1189 user
->handle
->channels
= user
->u_next
;
1191 user
->u_next
->u_prev
= user
->u_prev
;
1195 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1196 unregister_channel(channel
, "lost all users.");
1199 static void expire_ban(void *data
);
1201 static struct banData
*
1202 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1205 unsigned int ii
, l1
, l2
;
1210 bd
= malloc(sizeof(struct banData
));
1212 bd
->channel
= channel
;
1214 bd
->triggered
= triggered
;
1215 bd
->expires
= expires
;
1217 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1219 extern const char *hidden_host_suffix
;
1220 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1224 l2
= strlen(old_name
);
1227 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1229 new_mask
= alloca(MAXLEN
);
1230 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1233 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1235 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1236 bd
->reason
= strdup(reason
);
1239 timeq_add(expires
, expire_ban
, bd
);
1242 bd
->next
= channel
->bans
;
1244 channel
->bans
->prev
= bd
;
1246 channel
->banCount
++;
1253 del_channel_ban(struct banData
*ban
)
1255 ban
->channel
->banCount
--;
1259 ban
->prev
->next
= ban
->next
;
1261 ban
->channel
->bans
= ban
->next
;
1264 ban
->next
->prev
= ban
->prev
;
1267 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1276 expire_ban(void *data
)
1278 struct banData
*bd
= data
;
1279 if(!IsSuspended(bd
->channel
))
1281 struct banList bans
;
1282 struct mod_chanmode change
;
1284 bans
= bd
->channel
->channel
->banlist
;
1285 mod_chanmode_init(&change
);
1286 for(ii
=0; ii
<bans
.used
; ii
++)
1288 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1291 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1292 change
.args
[0].u
.hostmask
= bd
->mask
;
1293 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1299 del_channel_ban(bd
);
1302 static void chanserv_expire_suspension(void *data
);
1305 unregister_channel(struct chanData
*channel
, const char *reason
)
1307 struct mod_chanmode change
;
1308 char msgbuf
[MAXLEN
];
1310 /* After channel unregistration, the following must be cleaned
1312 - Channel information.
1315 - Channel suspension data.
1316 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1322 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1326 mod_chanmode_init(&change
);
1327 change
.modes_clear
|= MODE_REGISTERED
;
1328 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1331 while(channel
->users
)
1332 del_channel_user(channel
->users
, 0);
1334 while(channel
->bans
)
1335 del_channel_ban(channel
->bans
);
1337 free(channel
->topic
);
1338 free(channel
->registrar
);
1339 free(channel
->greeting
);
1340 free(channel
->user_greeting
);
1341 free(channel
->topic_mask
);
1344 channel
->prev
->next
= channel
->next
;
1346 channelList
= channel
->next
;
1349 channel
->next
->prev
= channel
->prev
;
1351 if(channel
->suspended
)
1353 struct chanNode
*cNode
= channel
->channel
;
1354 struct suspended
*suspended
, *next_suspended
;
1356 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1358 next_suspended
= suspended
->previous
;
1359 free(suspended
->suspender
);
1360 free(suspended
->reason
);
1361 if(suspended
->expires
)
1362 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1367 cNode
->channel_info
= NULL
;
1369 channel
->channel
->channel_info
= NULL
;
1371 dict_delete(channel
->notes
);
1372 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1373 if(!IsSuspended(channel
))
1374 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1375 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1376 UnlockChannel(channel
->channel
);
1378 registered_channels
--;
1382 expire_channels(UNUSED_ARG(void *data
))
1384 struct chanData
*channel
, *next
;
1385 struct userData
*user
;
1386 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1388 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1389 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1391 for(channel
= channelList
; channel
; channel
= next
)
1393 next
= channel
->next
;
1395 /* See if the channel can be expired. */
1396 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1397 || IsProtected(channel
))
1400 /* Make sure there are no high-ranking users still in the channel. */
1401 for(user
=channel
->users
; user
; user
=user
->next
)
1402 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1407 /* Unregister the channel */
1408 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1409 unregister_channel(channel
, "registration expired.");
1412 if(chanserv_conf
.channel_expire_frequency
)
1413 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1417 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1419 char protect
= channel
->chOpts
[chProtect
];
1420 struct userData
*cs_victim
, *cs_aggressor
;
1422 /* Don't protect if no one is to be protected, someone is attacking
1423 himself, or if the aggressor is an IRC Operator. */
1424 if(protect
== 'n' || victim
== aggressor
|| IsOper(aggressor
))
1427 /* Don't protect if the victim isn't authenticated (because they
1428 can't be a channel user), unless we are to protect non-users
1430 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1431 if(protect
!= 'a' && !cs_victim
)
1434 /* Protect if the aggressor isn't a user because at this point,
1435 the aggressor can only be less than or equal to the victim. */
1436 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1440 /* If the aggressor was a user, then the victim can't be helped. */
1447 if(cs_victim
->access
> cs_aggressor
->access
)
1452 if(cs_victim
->access
>= cs_aggressor
->access
)
1461 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1463 struct chanData
*cData
= channel
->channel_info
;
1464 struct userData
*cs_victim
;
1466 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1467 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveOps
]))
1468 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1470 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1478 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1480 struct chanData
*cData
= channel
->channel_info
;
1481 struct userData
*cs_victim
;
1483 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1484 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveHalfOps
]))
1485 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1487 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1496 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1498 if(IsService(victim
))
1500 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1504 if(protect_user(victim
, user
, channel
->channel_info
))
1506 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1514 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1516 if(IsService(victim
))
1518 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1522 if(protect_user(victim
, user
, channel
->channel_info
))
1524 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1531 static struct do_not_register
*
1532 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1534 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1535 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1536 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1537 strcpy(dnr
->reason
, reason
);
1539 if(dnr
->chan_name
[0] == '*')
1540 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1541 else if(strpbrk(dnr
->chan_name
, "*?"))
1542 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1544 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1548 static struct dnrList
1549 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1551 struct dnrList list
;
1553 struct do_not_register
*dnr
;
1555 dnrList_init(&list
);
1556 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1557 dnrList_append(&list
, dnr
);
1558 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1559 dnrList_append(&list
, dnr
);
1561 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1562 if(match_ircglob(chan_name
, iter_key(it
)))
1563 dnrList_append(&list
, iter_data(it
));
1568 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1570 struct dnrList list
;
1571 struct do_not_register
*dnr
;
1573 char buf
[INTERVALLEN
];
1575 list
= chanserv_find_dnrs(chan_name
, handle
);
1576 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1578 dnr
= list
.list
[ii
];
1581 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1582 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1585 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1588 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1593 struct do_not_register
*
1594 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1596 struct do_not_register
*dnr
;
1599 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1603 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1605 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1606 if(match_ircglob(chan_name
, iter_key(it
)))
1607 return iter_data(it
);
1612 static CHANSERV_FUNC(cmd_noregister
)
1615 struct do_not_register
*dnr
;
1616 char buf
[INTERVALLEN
];
1617 unsigned int matches
;
1623 reply("CSMSG_DNR_SEARCH_RESULTS");
1626 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1628 dnr
= iter_data(it
);
1630 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1632 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1635 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1637 dnr
= iter_data(it
);
1639 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1641 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1644 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1646 dnr
= iter_data(it
);
1648 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1650 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1655 reply("MSG_MATCH_COUNT", matches
);
1657 reply("MSG_NO_MATCHES");
1663 if(!IsChannelName(target
) && (*target
!= '*'))
1665 reply("CSMSG_NOT_DNR", target
);
1671 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1672 if((*target
== '*') && !get_handle_info(target
+ 1))
1674 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1677 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1678 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1682 reply("CSMSG_DNR_SEARCH_RESULTS");
1685 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1687 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1689 reply("MSG_NO_MATCHES");
1693 static CHANSERV_FUNC(cmd_allowregister
)
1695 const char *chan_name
= argv
[1];
1697 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1699 dict_remove(handle_dnrs
, chan_name
+1);
1700 reply("CSMSG_DNR_REMOVED", chan_name
);
1702 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1704 dict_remove(plain_dnrs
, chan_name
);
1705 reply("CSMSG_DNR_REMOVED", chan_name
);
1707 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1709 dict_remove(mask_dnrs
, chan_name
);
1710 reply("CSMSG_DNR_REMOVED", chan_name
);
1714 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1721 chanserv_get_owned_count(struct handle_info
*hi
)
1723 struct userData
*cList
;
1726 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1727 if(cList
->access
== UL_OWNER
)
1732 static CHANSERV_FUNC(cmd_register
)
1734 struct handle_info
*handle
;
1735 struct chanData
*cData
;
1736 struct modeNode
*mn
;
1737 char reason
[MAXLEN
];
1739 unsigned int new_channel
, force
=0;
1740 struct do_not_register
*dnr
;
1744 if(channel
->channel_info
)
1746 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1750 if(channel
->bad_channel
)
1752 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1757 && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1759 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1764 chan_name
= channel
->name
;
1770 reply("MSG_MISSING_PARAMS", cmd
->name
);
1771 svccmd_send_help_brief(user
, chanserv
, cmd
);
1774 if(!IsChannelName(argv
[1]))
1776 reply("MSG_NOT_CHANNEL_NAME");
1780 if(opserv_bad_channel(argv
[1]))
1782 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1787 chan_name
= argv
[1];
1790 if(argc
>= (new_channel
+2))
1792 if(!IsHelping(user
))
1794 reply("CSMSG_PROXY_FORBIDDEN");
1798 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1800 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1801 dnr
= chanserv_is_dnr(chan_name
, handle
);
1805 handle
= user
->handle_info
;
1806 dnr
= chanserv_is_dnr(chan_name
, handle
);
1810 if(!IsHelping(user
))
1811 reply("CSMSG_DNR_CHANNEL", chan_name
);
1813 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1817 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1819 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1824 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1826 cData
= register_channel(channel
, user
->handle_info
->handle
);
1827 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1828 cData
->modes
= chanserv_conf
.default_modes
;
1830 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1831 if (IsOffChannel(cData
))
1833 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
1837 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
1838 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1839 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1841 mod_chanmode_announce(chanserv
, channel
, change
);
1842 mod_chanmode_free(change
);
1845 /* Initialize the channel's max user record. */
1846 cData
->max
= channel
->members
.used
;
1848 if(handle
!= user
->handle_info
)
1849 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1851 reply("CSMSG_REG_SUCCESS", channel
->name
);
1853 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1854 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1859 make_confirmation_string(struct userData
*uData
)
1861 static char strbuf
[16];
1866 for(src
= uData
->handle
->handle
; *src
; )
1867 accum
= accum
* 31 + toupper(*src
++);
1869 for(src
= uData
->channel
->channel
->name
; *src
; )
1870 accum
= accum
* 31 + toupper(*src
++);
1871 sprintf(strbuf
, "%08x", accum
);
1875 static CHANSERV_FUNC(cmd_unregister
)
1878 char reason
[MAXLEN
];
1879 struct chanData
*cData
;
1880 struct userData
*uData
;
1882 cData
= channel
->channel_info
;
1885 reply("CSMSG_NOT_REGISTERED", channel
->name
);
1889 uData
= GetChannelUser(cData
, user
->handle_info
);
1890 if(!uData
|| (uData
->access
< UL_OWNER
))
1892 reply("CSMSG_NO_ACCESS");
1896 if(IsProtected(cData
))
1898 reply("CSMSG_UNREG_NODELETE", channel
->name
);
1902 if(!IsHelping(user
))
1904 const char *confirm_string
;
1905 if(IsSuspended(cData
))
1907 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
1910 confirm_string
= make_confirmation_string(uData
);
1911 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
1913 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
1918 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
1919 name
= strdup(channel
->name
);
1920 unregister_channel(cData
, reason
);
1921 reply("CSMSG_UNREG_SUCCESS", name
);
1926 static CHANSERV_FUNC(cmd_move
)
1928 struct mod_chanmode change
;
1929 struct chanNode
*target
;
1930 struct modeNode
*mn
;
1931 struct userData
*uData
;
1932 char reason
[MAXLEN
];
1933 struct do_not_register
*dnr
;
1937 if(IsProtected(channel
->channel_info
))
1939 reply("CSMSG_MOVE_NODELETE", channel
->name
);
1943 if(!IsChannelName(argv
[1]))
1945 reply("MSG_NOT_CHANNEL_NAME");
1949 if(opserv_bad_channel(argv
[1]))
1951 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1955 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
1957 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
1959 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
1961 if(!IsHelping(user
))
1962 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
1964 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
1970 mod_chanmode_init(&change
);
1971 if(!(target
= GetChannel(argv
[1])))
1973 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1974 if(!IsSuspended(channel
->channel_info
))
1975 AddChannelUser(chanserv
, target
);
1977 else if(target
->channel_info
)
1979 reply("CSMSG_ALREADY_REGGED", target
->name
);
1982 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
1983 && !IsHelping(user
))
1985 reply("CSMSG_MUST_BE_OPPED", target
->name
);
1988 else if(!IsSuspended(channel
->channel_info
))
1991 change
.args
[0].mode
= MODE_CHANOP
;
1992 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
1993 mod_chanmode_announce(chanserv
, target
, &change
);
1998 /* Clear MODE_REGISTERED from old channel, add it to new. */
2000 change
.modes_clear
= MODE_REGISTERED
;
2001 mod_chanmode_announce(chanserv
, channel
, &change
);
2002 change
.modes_clear
= 0;
2003 change
.modes_set
= MODE_REGISTERED
;
2004 mod_chanmode_announce(chanserv
, target
, &change
);
2007 /* Move the channel_info to the target channel; it
2008 shouldn't be necessary to clear timeq callbacks
2009 for the old channel. */
2010 target
->channel_info
= channel
->channel_info
;
2011 target
->channel_info
->channel
= target
;
2012 channel
->channel_info
= NULL
;
2014 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2016 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2017 if(!IsSuspended(target
->channel_info
))
2019 char reason2
[MAXLEN
];
2020 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2021 DelChannelUser(chanserv
, channel
, reason2
, 0);
2023 UnlockChannel(channel
);
2024 LockChannel(target
);
2025 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2030 merge_users(struct chanData
*source
, struct chanData
*target
)
2032 struct userData
*suData
, *tuData
, *next
;
2038 /* Insert the source's users into the scratch area. */
2039 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2040 dict_insert(merge
, suData
->handle
->handle
, suData
);
2042 /* Iterate through the target's users, looking for
2043 users common to both channels. The lower access is
2044 removed from either the scratch area or target user
2046 for(tuData
= target
->users
; tuData
; tuData
= next
)
2048 struct userData
*choice
;
2050 next
= tuData
->next
;
2052 /* If a source user exists with the same handle as a target
2053 channel's user, resolve the conflict by removing one. */
2054 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2058 /* Pick the data we want to keep. */
2059 /* If the access is the same, use the later seen time. */
2060 if(suData
->access
== tuData
->access
)
2061 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2062 else /* Otherwise, keep the higher access level. */
2063 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2065 /* Remove the user that wasn't picked. */
2066 if(choice
== tuData
)
2068 dict_remove(merge
, suData
->handle
->handle
);
2069 del_channel_user(suData
, 0);
2072 del_channel_user(tuData
, 0);
2075 /* Move the remaining users to the target channel. */
2076 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2078 suData
= iter_data(it
);
2080 /* Insert the user into the target channel's linked list. */
2081 suData
->prev
= NULL
;
2082 suData
->next
= target
->users
;
2083 suData
->channel
= target
;
2086 target
->users
->prev
= suData
;
2087 target
->users
= suData
;
2089 /* Update the user counts for the target channel; the
2090 source counts are left alone. */
2091 target
->userCount
++;
2094 /* Possible to assert (source->users == NULL) here. */
2095 source
->users
= NULL
;
2100 merge_bans(struct chanData
*source
, struct chanData
*target
)
2102 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2104 /* Hold on to the original head of the target ban list
2105 to avoid comparing source bans with source bans. */
2106 tFront
= target
->bans
;
2108 /* Perform a totally expensive O(n*m) merge, ick. */
2109 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2111 /* Flag to track whether the ban's been moved
2112 to the destination yet. */
2115 /* Possible to assert (sbData->prev == NULL) here. */
2116 sNext
= sbData
->next
;
2118 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2120 tNext
= tbData
->next
;
2122 /* Perform two comparisons between each source
2123 and target ban, conflicts are resolved by
2124 keeping the broader ban and copying the later
2125 expiration and triggered time. */
2126 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2128 /* There is a broader ban in the target channel that
2129 overrides one in the source channel; remove the
2130 source ban and break. */
2131 if(sbData
->expires
> tbData
->expires
)
2132 tbData
->expires
= sbData
->expires
;
2133 if(sbData
->triggered
> tbData
->triggered
)
2134 tbData
->triggered
= sbData
->triggered
;
2135 del_channel_ban(sbData
);
2138 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2140 /* There is a broader ban in the source channel that
2141 overrides one in the target channel; remove the
2142 target ban, fall through and move the source over. */
2143 if(tbData
->expires
> sbData
->expires
)
2144 sbData
->expires
= tbData
->expires
;
2145 if(tbData
->triggered
> sbData
->triggered
)
2146 sbData
->triggered
= tbData
->triggered
;
2147 if(tbData
== tFront
)
2149 del_channel_ban(tbData
);
2152 /* Source bans can override multiple target bans, so
2153 we allow a source to run through this loop multiple
2154 times, but we can only move it once. */
2159 /* Remove the source ban from the source ban list. */
2161 sbData
->next
->prev
= sbData
->prev
;
2163 /* Modify the source ban's associated channel. */
2164 sbData
->channel
= target
;
2166 /* Insert the ban into the target channel's linked list. */
2167 sbData
->prev
= NULL
;
2168 sbData
->next
= target
->bans
;
2171 target
->bans
->prev
= sbData
;
2172 target
->bans
= sbData
;
2174 /* Update the user counts for the target channel. */
2179 /* Possible to assert (source->bans == NULL) here. */
2180 source
->bans
= NULL
;
2184 merge_data(struct chanData
*source
, struct chanData
*target
)
2186 if(source
->visited
> target
->visited
)
2187 target
->visited
= source
->visited
;
2191 merge_channel(struct chanData
*source
, struct chanData
*target
)
2193 merge_users(source
, target
);
2194 merge_bans(source
, target
);
2195 merge_data(source
, target
);
2198 static CHANSERV_FUNC(cmd_merge
)
2200 struct userData
*target_user
;
2201 struct chanNode
*target
;
2202 char reason
[MAXLEN
];
2206 /* Make sure the target channel exists and is registered to the user
2207 performing the command. */
2208 if(!(target
= GetChannel(argv
[1])))
2210 reply("MSG_INVALID_CHANNEL");
2214 if(!target
->channel_info
)
2216 reply("CSMSG_NOT_REGISTERED", target
->name
);
2220 if(IsProtected(channel
->channel_info
))
2222 reply("CSMSG_MERGE_NODELETE");
2226 if(IsSuspended(target
->channel_info
))
2228 reply("CSMSG_MERGE_SUSPENDED");
2232 if(channel
== target
)
2234 reply("CSMSG_MERGE_SELF");
2238 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2239 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2241 reply("CSMSG_MERGE_NOT_OWNER");
2245 /* Merge the channel structures and associated data. */
2246 merge_channel(channel
->channel_info
, target
->channel_info
);
2247 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2248 unregister_channel(channel
->channel_info
, reason
);
2249 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2253 static CHANSERV_FUNC(cmd_opchan
)
2255 struct mod_chanmode change
;
2256 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2258 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2261 channel
->channel_info
->may_opchan
= 0;
2262 mod_chanmode_init(&change
);
2264 change
.args
[0].mode
= MODE_CHANOP
;
2265 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2266 mod_chanmode_announce(chanserv
, channel
, &change
);
2267 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2271 static CHANSERV_FUNC(cmd_adduser
)
2273 struct userData
*actee
;
2274 struct userData
*actor
;
2275 struct handle_info
*handle
;
2276 unsigned short access
;
2280 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2282 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2286 access
= user_level_from_name(argv
[2], UL_OWNER
);
2289 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2293 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2294 if(actor
->access
<= access
)
2296 reply("CSMSG_NO_BUMP_ACCESS");
2300 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2303 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2305 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2309 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2310 scan_user_presence(actee
, NULL
);
2311 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2315 static CHANSERV_FUNC(cmd_clvl
)
2317 struct handle_info
*handle
;
2318 struct userData
*victim
;
2319 struct userData
*actor
;
2320 unsigned short new_access
;
2321 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2325 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2327 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2330 if(handle
== user
->handle_info
&& !privileged
)
2332 reply("CSMSG_NO_SELF_CLVL");
2336 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2338 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2342 if(actor
->access
<= victim
->access
&& !privileged
)
2344 reply("MSG_USER_OUTRANKED", handle
->handle
);
2348 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2352 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2356 if(new_access
>= actor
->access
&& !privileged
)
2358 reply("CSMSG_NO_BUMP_ACCESS");
2362 victim
->access
= new_access
;
2363 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2367 static CHANSERV_FUNC(cmd_deluser
)
2369 struct handle_info
*handle
;
2370 struct userData
*victim
;
2371 struct userData
*actor
;
2372 unsigned short access
;
2377 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2379 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2382 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2384 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2390 access
= user_level_from_name(argv
[1], UL_OWNER
);
2393 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2396 if(access
!= victim
->access
)
2398 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2404 access
= victim
->access
;
2407 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2409 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2413 chan_name
= strdup(channel
->name
);
2414 del_channel_user(victim
, 1);
2415 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2421 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2423 struct userData
*actor
, *uData
, *next
;
2425 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2427 if(min_access
> max_access
)
2429 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2433 if((actor
->access
<= max_access
) && !IsHelping(user
))
2435 reply("CSMSG_NO_ACCESS");
2439 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2443 if((uData
->access
>= min_access
)
2444 && (uData
->access
<= max_access
)
2445 && match_ircglob(uData
->handle
->handle
, mask
))
2446 del_channel_user(uData
, 1);
2449 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2453 static CHANSERV_FUNC(cmd_mdelowner
)
2455 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2458 static CHANSERV_FUNC(cmd_mdelcoowner
)
2460 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2463 static CHANSERV_FUNC(cmd_mdelmanager
)
2465 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2468 static CHANSERV_FUNC(cmd_mdelop
)
2470 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2473 static CHANSERV_FUNC(cmd_mdelpeon
)
2475 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2478 static CHANSERV_FUNC(cmd_mdelhalfop
)
2480 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2485 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2487 struct banData
*bData
, *next
;
2488 char interval
[INTERVALLEN
];
2493 limit
= now
- duration
;
2494 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2498 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2501 del_channel_ban(bData
);
2505 intervalString(interval
, duration
, user
->handle_info
);
2506 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2511 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2513 struct userData
*actor
, *uData
, *next
;
2514 char interval
[INTERVALLEN
];
2518 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2519 if(min_access
> max_access
)
2521 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2525 if((actor
->access
<= max_access
) && !IsHelping(user
))
2527 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2532 limit
= now
- duration
;
2533 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2537 if((uData
->seen
> limit
) || uData
->present
)
2540 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2541 || (!max_access
&& (uData
->access
< actor
->access
)))
2543 del_channel_user(uData
, 1);
2551 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2553 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2557 static CHANSERV_FUNC(cmd_trim
)
2559 unsigned long duration
;
2560 unsigned short min_level
, max_level
;
2564 duration
= ParseInterval(argv
[2]);
2567 reply("CSMSG_CANNOT_TRIM");
2571 if(!irccasecmp(argv
[1], "bans"))
2573 cmd_trim_bans(user
, channel
, duration
);
2576 else if(!irccasecmp(argv
[1], "users"))
2578 cmd_trim_users(user
, channel
, 0, 0, duration
);
2581 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2583 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2586 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2588 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2593 reply("CSMSG_INVALID_TRIM", argv
[1]);
2598 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2599 to the user. cmd_all takes advantage of this. */
2600 static CHANSERV_FUNC(cmd_up
)
2602 struct mod_chanmode change
;
2603 struct userData
*uData
;
2606 mod_chanmode_init(&change
);
2608 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2609 if(!change
.args
[0].u
.member
)
2612 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2616 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2620 reply("CSMSG_GODMODE_UP", argv
[0]);
2623 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveOps
])
2625 change
.args
[0].mode
= MODE_CHANOP
;
2626 errmsg
= "CSMSG_ALREADY_OPPED";
2628 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
2630 change
.args
[0].mode
= MODE_HALFOP
;
2631 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2633 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveVoice
])
2635 change
.args
[0].mode
= MODE_VOICE
;
2636 errmsg
= "CSMSG_ALREADY_VOICED";
2641 reply("CSMSG_NO_ACCESS");
2644 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2645 if(!change
.args
[0].mode
)
2648 reply(errmsg
, channel
->name
);
2651 modcmd_chanmode_announce(&change
);
2655 static CHANSERV_FUNC(cmd_down
)
2657 struct mod_chanmode change
;
2659 mod_chanmode_init(&change
);
2661 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2662 if(!change
.args
[0].u
.member
)
2665 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2669 if(!change
.args
[0].u
.member
->modes
)
2672 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2676 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2677 modcmd_chanmode_announce(&change
);
2681 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
)
2683 struct userData
*cList
;
2685 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2687 if(IsSuspended(cList
->channel
)
2688 || IsUserSuspended(cList
)
2689 || !GetUserMode(cList
->channel
->channel
, user
))
2692 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2698 static CHANSERV_FUNC(cmd_upall
)
2700 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2703 static CHANSERV_FUNC(cmd_downall
)
2705 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2708 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2709 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2712 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
)
2714 unsigned int ii
, valid
;
2715 struct userNode
*victim
;
2716 struct mod_chanmode
*change
;
2718 change
= mod_chanmode_alloc(argc
- 1);
2720 for(ii
=valid
=0; ++ii
< argc
; )
2722 if(!(victim
= GetUserH(argv
[ii
])))
2724 change
->args
[valid
].mode
= mode
;
2725 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2726 if(!change
->args
[valid
].u
.member
)
2728 if(validate
&& !validate(user
, channel
, victim
))
2733 change
->argc
= valid
;
2734 if(valid
< (argc
-1))
2735 reply("CSMSG_PROCESS_FAILED");
2738 modcmd_chanmode_announce(change
);
2739 reply(action
, channel
->name
);
2741 mod_chanmode_free(change
);
2745 static CHANSERV_FUNC(cmd_op
)
2747 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2750 static CHANSERV_FUNC(cmd_hop
)
2752 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2755 static CHANSERV_FUNC(cmd_deop
)
2757 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2760 static CHANSERV_FUNC(cmd_dehop
)
2762 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2765 static CHANSERV_FUNC(cmd_voice
)
2767 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2770 static CHANSERV_FUNC(cmd_devoice
)
2772 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2776 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2782 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2784 struct modeNode
*mn
= channel
->members
.list
[ii
];
2786 if(IsService(mn
->user
))
2789 if(!user_matches_glob(mn
->user
, ban
, 1))
2792 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2796 victims
[(*victimCount
)++] = mn
;
2802 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2804 struct userNode
*victim
;
2805 struct modeNode
**victims
;
2806 unsigned int offset
, n
, victimCount
, duration
= 0;
2807 char *reason
= "Bye.", *ban
, *name
;
2808 char interval
[INTERVALLEN
];
2810 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2811 REQUIRE_PARAMS(offset
);
2814 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2815 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2817 /* Truncate the reason to a length of TOPICLEN, as
2818 the ircd does; however, leave room for an ellipsis
2819 and the kicker's nick. */
2820 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2824 if((victim
= GetUserH(argv
[1])))
2826 victims
= alloca(sizeof(victims
[0]));
2827 victims
[0] = GetUserMode(channel
, victim
);
2828 /* XXX: The comparison with ACTION_KICK is just because all
2829 * other actions can work on users outside the channel, and we
2830 * want to allow those (e.g. unbans) in that case. If we add
2831 * some other ejection action for in-channel users, change
2833 victimCount
= victims
[0] ? 1 : 0;
2835 if(IsService(victim
))
2837 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
2841 if((action
== ACTION_KICK
) && !victimCount
)
2843 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
2847 if(protect_user(victim
, user
, channel
->channel_info
))
2849 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2853 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
2854 name
= victim
->nick
;
2858 if(!is_ircmask(argv
[1]))
2860 reply("MSG_NICK_UNKNOWN", argv
[1]);
2864 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
2866 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
2868 reply("CSMSG_MASK_PROTECTED", argv
[1]);
2871 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
2873 victimCount
= 0; /* Dont deop etc ppl who match this */
2875 #ifdef entropy_lameness
2876 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
2878 reply("CSMSG_LAME_MASK", argv
[1]);
2883 if((action
== ACTION_KICK
) && (victimCount
== 0))
2885 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
2889 name
= ban
= strdup(argv
[1]);
2892 /* Truncate the ban in place if necessary; we must ensure
2893 that 'ban' is a valid ban mask before sanitizing it. */
2894 sanitize_ircmask(ban
);
2896 if(action
& ACTION_ADD_BAN
)
2898 struct banData
*bData
, *next
;
2900 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
2902 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
2907 if(action
& ACTION_ADD_TIMED_BAN
)
2909 duration
= ParseInterval(argv
[2]);
2913 reply("CSMSG_DURATION_TOO_LOW");
2917 else if(duration
> (86400 * 365 * 2))
2919 reply("CSMSG_DURATION_TOO_HIGH");
2925 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2927 if(match_ircglobs(bData
->mask
, ban
))
2929 int exact
= !irccasecmp(bData
->mask
, ban
);
2931 /* The ban is redundant; there is already a ban
2932 with the same effect in place. */
2936 free(bData
->reason
);
2937 bData
->reason
= strdup(reason
);
2938 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
2940 reply("CSMSG_REASON_CHANGE", ban
);
2944 if(exact
&& bData
->expires
)
2948 /* If the ban matches an existing one exactly,
2949 extend the expiration time if the provided
2950 duration is longer. */
2951 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
2953 bData
->expires
= now
+ duration
;
2964 /* Delete the expiration timeq entry and
2965 requeue if necessary. */
2966 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
2969 timeq_add(bData
->expires
, expire_ban
, bData
);
2973 /* automated kickban */
2976 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
2978 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2984 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2991 if(match_ircglobs(ban
, bData
->mask
))
2993 /* The ban we are adding makes previously existing
2994 bans redundant; silently remove them. */
2995 del_channel_ban(bData
);
2999 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
);
3001 name
= ban
= strdup(bData
->mask
);
3005 /* WHAT DOES THIS DO?? -Rubin */
3006 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3008 extern const char *hidden_host_suffix
;
3009 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3011 unsigned int l1
, l2
;
3014 l2
= strlen(old_name
);
3017 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3019 new_mask
= malloc(MAXLEN
);
3020 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3022 name
= ban
= new_mask
;
3027 if(action
& ACTION_BAN
)
3029 unsigned int exists
;
3030 struct mod_chanmode
*change
;
3032 if(channel
->banlist
.used
>= MAXBANS
)
3035 reply("CSMSG_BANLIST_FULL", channel
->name
);
3040 exists
= ChannelBanExists(channel
, ban
);
3041 change
= mod_chanmode_alloc(victimCount
+ 1);
3042 for(n
= 0; n
< victimCount
; ++n
)
3044 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3045 change
->args
[n
].u
.member
= victims
[n
];
3049 change
->args
[n
].mode
= MODE_BAN
;
3050 change
->args
[n
++].u
.hostmask
= ban
;
3054 modcmd_chanmode_announce(change
);
3056 mod_chanmode_announce(chanserv
, channel
, change
);
3057 mod_chanmode_free(change
);
3059 if(exists
&& (action
== ACTION_BAN
))
3062 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3068 if(action
& ACTION_KICK
)
3070 char kick_reason
[MAXLEN
];
3071 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3073 for(n
= 0; n
< victimCount
; n
++)
3074 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3079 /* No response, since it was automated. */
3081 else if(action
& ACTION_ADD_BAN
)
3084 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3086 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3088 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3089 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3090 else if(action
& ACTION_BAN
)
3091 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3092 else if(action
& ACTION_KICK
&& victimCount
)
3093 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3099 static CHANSERV_FUNC(cmd_kickban
)
3101 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3104 static CHANSERV_FUNC(cmd_kick
)
3106 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3109 static CHANSERV_FUNC(cmd_ban
)
3111 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3114 static CHANSERV_FUNC(cmd_addban
)
3116 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
3119 static CHANSERV_FUNC(cmd_addtimedban
)
3121 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
3124 static struct mod_chanmode
*
3125 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3127 struct mod_chanmode
*change
;
3128 unsigned char *match
;
3129 unsigned int ii
, count
;
3131 match
= alloca(bans
->used
);
3134 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3136 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3143 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3145 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3152 change
= mod_chanmode_alloc(count
);
3153 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3157 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3158 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3160 assert(count
== change
->argc
);
3165 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3167 struct userNode
*actee
;
3173 /* may want to allow a comma delimited list of users... */
3174 if(!(actee
= GetUserH(argv
[1])))
3176 if(!is_ircmask(argv
[1]))
3178 reply("MSG_NICK_UNKNOWN", argv
[1]);
3182 mask
= strdup(argv
[1]);
3185 /* We don't sanitize the mask here because ircu
3187 if(action
& ACTION_UNBAN
)
3189 struct mod_chanmode
*change
;
3190 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3195 for(ii
= 0; ii
< change
->argc
; ++ii
)
3196 free((char*)change
->args
[ii
].u
.hostmask
);
3197 modcmd_chanmode_announce(change
);
3198 mod_chanmode_free(change
);
3203 if(action
& ACTION_DEL_BAN
)
3205 struct banData
*ban
, *next
;
3207 ban
= channel
->channel_info
->bans
;
3211 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3214 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3219 del_channel_ban(ban
);
3226 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3228 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3234 static CHANSERV_FUNC(cmd_unban
)
3236 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3239 static CHANSERV_FUNC(cmd_delban
)
3241 /* it doesn't necessarily have to remove the channel ban - may want
3242 to make that an option. */
3243 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3246 static CHANSERV_FUNC(cmd_unbanme
)
3248 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3249 long flags
= ACTION_UNBAN
;
3251 /* remove permanent bans if the user has the proper access. */
3252 if(uData
->access
>= UL_MANAGER
)
3253 flags
|= ACTION_DEL_BAN
;
3255 argv
[1] = user
->nick
;
3256 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3259 static CHANSERV_FUNC(cmd_unbanall
)
3261 struct mod_chanmode
*change
;
3264 if(!channel
->banlist
.used
)
3266 reply("CSMSG_NO_BANS", channel
->name
);
3270 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3271 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3273 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3274 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3276 modcmd_chanmode_announce(change
);
3277 for(ii
= 0; ii
< change
->argc
; ++ii
)
3278 free((char*)change
->args
[ii
].u
.hostmask
);
3279 mod_chanmode_free(change
);
3280 reply("CSMSG_BANS_REMOVED", channel
->name
);
3284 static CHANSERV_FUNC(cmd_open
)
3286 struct mod_chanmode
*change
;
3289 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3291 change
= mod_chanmode_alloc(0);
3292 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3293 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3294 && channel
->channel_info
->modes
.modes_set
)
3295 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3296 modcmd_chanmode_announce(change
);
3297 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3298 for(ii
= 0; ii
< change
->argc
; ++ii
)
3299 free((char*)change
->args
[ii
].u
.hostmask
);
3300 mod_chanmode_free(change
);
3304 static CHANSERV_FUNC(cmd_myaccess
)
3306 static struct string_buffer sbuf
;
3307 struct handle_info
*target_handle
;
3308 struct userData
*uData
;
3311 target_handle
= user
->handle_info
;
3312 else if(!IsHelping(user
))
3314 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3317 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3320 if(!target_handle
->channels
)
3322 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3326 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3327 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3329 struct chanData
*cData
= uData
->channel
;
3331 if(uData
->access
> UL_OWNER
)
3333 if(IsProtected(cData
)
3334 && (target_handle
!= user
->handle_info
)
3335 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3338 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3339 if(uData
->flags
!= USER_AUTO_OP
)
3340 string_buffer_append(&sbuf
, ',');
3341 if(IsUserSuspended(uData
))
3342 string_buffer_append(&sbuf
, 's');
3343 if(IsUserAutoOp(uData
))
3345 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
3346 string_buffer_append(&sbuf
, 'o');
3347 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
3348 string_buffer_append(&sbuf
, 'h');
3349 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
3350 string_buffer_append(&sbuf
, 'v');
3352 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3353 string_buffer_append(&sbuf
, 'i');
3355 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3357 string_buffer_append_string(&sbuf
, ")]");
3358 string_buffer_append(&sbuf
, '\0');
3359 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3365 static CHANSERV_FUNC(cmd_access
)
3367 struct userNode
*target
;
3368 struct handle_info
*target_handle
;
3369 struct userData
*uData
;
3371 char prefix
[MAXLEN
];
3376 target_handle
= target
->handle_info
;
3378 else if((target
= GetUserH(argv
[1])))
3380 target_handle
= target
->handle_info
;
3382 else if(argv
[1][0] == '*')
3384 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3386 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3392 reply("MSG_NICK_UNKNOWN", argv
[1]);
3396 assert(target
|| target_handle
);
3398 if(target
== chanserv
)
3400 reply("CSMSG_IS_CHANSERV");
3408 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3413 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3416 reply("MSG_AUTHENTICATE");
3422 const char *epithet
= NULL
, *type
= NULL
;
3425 epithet
= chanserv_conf
.irc_operator_epithet
;
3428 else if(IsNetworkHelper(target
))
3430 epithet
= chanserv_conf
.network_helper_epithet
;
3431 type
= "network helper";
3433 else if(IsSupportHelper(target
))
3435 epithet
= chanserv_conf
.support_helper_epithet
;
3436 type
= "support helper";
3440 if(target_handle
->epithet
)
3441 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3443 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3445 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3449 sprintf(prefix
, "%s", target_handle
->handle
);
3452 if(!channel
->channel_info
)
3454 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3458 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3459 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3460 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3462 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3463 /* To prevent possible information leaks, only show infolines
3464 * if the requestor is in the channel or it's their own
3466 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3468 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3470 /* Likewise, only say it's suspended if the user has active
3471 * access in that channel or it's their own entry. */
3472 if(IsUserSuspended(uData
)
3473 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3474 || (user
->handle_info
== uData
->handle
)))
3476 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3481 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3487 /* This is never used... */
3489 zoot_list(struct listData
*list
)
3491 struct userData
*uData
;
3492 unsigned int start
, curr
, highest
, lowest
;
3493 struct helpfile_table tmp_table
;
3494 const char **temp
, *msg
;
3496 if(list
->table
.length
== 1)
3499 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
), list
->search
);
3501 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
));
3502 msg
= user_find_message(list
->user
, "MSG_NONE");
3503 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3505 tmp_table
.width
= list
->table
.width
;
3506 tmp_table
.flags
= list
->table
.flags
;
3507 list
->table
.contents
[0][0] = " ";
3508 highest
= list
->highest
;
3509 if(list
->lowest
!= 0)
3510 lowest
= list
->lowest
;
3511 else if(highest
< 100)
3514 lowest
= highest
- 100;
3515 for(start
= curr
= 1; curr
< list
->table
.length
; )
3517 uData
= list
->users
[curr
-1];
3518 list
->table
.contents
[curr
++][0] = " ";
3519 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3522 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, user_level_name_from_level(lowest
), user_level_name_from_level(highest
), list
->search
);
3524 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, user_level_name_from_level(lowest
), user_level_name_from_level(highest
));
3525 temp
= list
->table
.contents
[--start
];
3526 list
->table
.contents
[start
] = list
->table
.contents
[0];
3527 tmp_table
.contents
= list
->table
.contents
+ start
;
3528 tmp_table
.length
= curr
- start
;
3529 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3530 list
->table
.contents
[start
] = temp
;
3532 highest
= lowest
- 1;
3533 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3539 def_list(struct listData
*list
)
3543 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
), list
->search
);
3545 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
));
3546 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3547 if(list
->table
.length
== 1)
3549 msg
= user_find_message(list
->user
, "MSG_NONE");
3550 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3555 userData_access_comp(const void *arg_a
, const void *arg_b
)
3557 const struct userData
*a
= *(struct userData
**)arg_a
;
3558 const struct userData
*b
= *(struct userData
**)arg_b
;
3560 if(a
->access
!= b
->access
)
3561 res
= b
->access
- a
->access
;
3563 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3568 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3570 void (*send_list
)(struct listData
*);
3571 struct userData
*uData
;
3572 struct listData lData
;
3573 unsigned int matches
;
3577 lData
.bot
= cmd
->parent
->bot
;
3578 lData
.channel
= channel
;
3579 lData
.lowest
= lowest
;
3580 lData
.highest
= highest
;
3581 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3582 send_list
= def_list
;
3583 /* What does the following line do exactly?? */
3584 (void)zoot_list
; /* since it doesn't show user levels */
3586 /* this does nothing!! -rubin
3587 if(user->handle_info)
3589 switch(user->handle_info->userlist_style)
3591 case HI_STYLE_DEF: send_list = def_list; break;
3592 case HI_STYLE_ZOOT: send_list = def_list; break;
3597 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3599 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3601 if((uData
->access
< lowest
)
3602 || (uData
->access
> highest
)
3603 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3605 lData
.users
[matches
++] = uData
;
3607 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3609 lData
.table
.length
= matches
+1;
3610 lData
.table
.width
= 5;
3611 lData
.table
.flags
= TABLE_NO_FREE
;
3612 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3613 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3614 lData
.table
.contents
[0] = ary
;
3618 ary
[3] = "Last Seen";
3620 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3622 struct userData
*uData
= lData
.users
[matches
-1];
3623 char seen
[INTERVALLEN
];
3625 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3626 lData
.table
.contents
[matches
] = ary
;
3627 /* ary[0] = strtab(uData->access);*/
3628 ary
[0] = user_level_name_from_level(uData
->access
);
3629 /* TODO: replace above with func that returns static string
3630 * of userlevel for that level. eg OP/MANAGER etc. -rubin */
3631 ary
[1] = strtab(uData
->access
);
3632 ary
[2] = uData
->handle
->handle
;
3635 else if(!uData
->seen
)
3638 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3639 ary
[3] = strdup(ary
[3]);
3640 if(IsUserSuspended(uData
))
3641 ary
[4] = "Suspended";
3642 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3643 ary
[4] = "Vacation";
3648 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3650 free((char*)lData
.table
.contents
[matches
][3]);
3651 free(lData
.table
.contents
[matches
]);
3653 free(lData
.table
.contents
[0]);
3654 free(lData
.table
.contents
);
3658 static CHANSERV_FUNC(cmd_users
)
3660 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3663 static CHANSERV_FUNC(cmd_wlist
)
3665 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3668 static CHANSERV_FUNC(cmd_clist
)
3670 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3673 static CHANSERV_FUNC(cmd_mlist
)
3675 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3678 static CHANSERV_FUNC(cmd_olist
)
3680 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3683 static CHANSERV_FUNC(cmd_hlist
)
3685 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3688 static CHANSERV_FUNC(cmd_plist
)
3690 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3693 static CHANSERV_FUNC(cmd_bans
)
3695 struct helpfile_table tbl
;
3696 unsigned int matches
= 0, timed
= 0, ii
;
3697 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3698 const char *msg_never
, *triggered
, *expires
;
3699 struct banData
*ban
, **bans
;
3706 reply("CSMSG_BANS_HEADER", channel
->name
);
3707 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3709 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3711 if(search
&& !match_ircglobs(search
, ban
->mask
))
3713 bans
[matches
++] = ban
;
3718 tbl
.length
= matches
+ 1;
3719 tbl
.width
= 4 + timed
;
3721 tbl
.flags
= TABLE_NO_FREE
;
3722 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3723 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3724 tbl
.contents
[0][0] = "Mask";
3725 tbl
.contents
[0][1] = "Set By";
3726 tbl
.contents
[0][2] = "Triggered";
3729 tbl
.contents
[0][3] = "Expires";
3730 tbl
.contents
[0][4] = "Reason";
3733 tbl
.contents
[0][3] = "Reason";
3736 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3738 free(tbl
.contents
[0]);
3743 msg_never
= user_find_message(user
, "MSG_NEVER");
3744 for(ii
= 0; ii
< matches
; )
3750 else if(ban
->expires
)
3751 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3753 expires
= msg_never
;
3756 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3758 triggered
= msg_never
;
3760 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3761 tbl
.contents
[ii
][0] = ban
->mask
;
3762 tbl
.contents
[ii
][1] = ban
->owner
;
3763 tbl
.contents
[ii
][2] = strdup(triggered
);
3766 tbl
.contents
[ii
][3] = strdup(expires
);
3767 tbl
.contents
[ii
][4] = ban
->reason
;
3770 tbl
.contents
[ii
][3] = ban
->reason
;
3772 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3773 /* reply("MSG_MATCH_COUNT", matches); */
3774 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3776 free((char*)tbl
.contents
[ii
][2]);
3778 free((char*)tbl
.contents
[ii
][3]);
3779 free(tbl
.contents
[ii
]);
3781 free(tbl
.contents
[0]);
3787 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3789 struct chanData
*cData
= channel
->channel_info
;
3790 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3792 if(cData
->topic_mask
)
3793 return !match_ircglob(new_topic
, cData
->topic_mask
);
3794 else if(cData
->topic
)
3795 return irccasecmp(new_topic
, cData
->topic
);
3800 static CHANSERV_FUNC(cmd_topic
)
3802 struct chanData
*cData
;
3805 cData
= channel
->channel_info
;
3810 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
3811 reply("CSMSG_TOPIC_SET", cData
->topic
);
3815 reply("CSMSG_NO_TOPIC", channel
->name
);
3819 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
3820 /* If they say "!topic *", use an empty topic. */
3821 if((topic
[0] == '*') && (topic
[1] == 0))
3823 if(bad_topic(channel
, user
, topic
))
3825 char *topic_mask
= cData
->topic_mask
;
3828 char new_topic
[TOPICLEN
+1], tchar
;
3829 int pos
=0, starpos
=-1, dpos
=0, len
;
3831 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3838 len
= strlen(topic
);
3839 if((dpos
+ len
) > TOPICLEN
)
3840 len
= TOPICLEN
+ 1 - dpos
;
3841 memcpy(new_topic
+dpos
, topic
, len
);
3845 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
3846 default: new_topic
[dpos
++] = tchar
; break;
3849 if((dpos
> TOPICLEN
) || tchar
)
3852 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
3853 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
3856 new_topic
[dpos
] = 0;
3857 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
3859 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
3864 SetChannelTopic(channel
, chanserv
, topic
, 1);
3866 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
3868 /* Grab the topic and save it as the default topic. */
3870 cData
->topic
= strdup(channel
->topic
);
3876 static CHANSERV_FUNC(cmd_mode
)
3878 struct mod_chanmode
*change
;
3882 change
= &channel
->channel_info
->modes
;
3883 if(change
->modes_set
|| change
->modes_clear
) {
3884 modcmd_chanmode_announce(change
);
3885 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
3887 reply("CSMSG_NO_MODES", channel
->name
);
3891 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
3894 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
3898 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3899 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
3902 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
3903 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
3907 modcmd_chanmode_announce(change
);
3908 mod_chanmode_free(change
);
3909 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
3913 static CHANSERV_FUNC(cmd_invite
)
3915 struct userData
*uData
;
3916 struct userNode
*invite
;
3918 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3922 if(!(invite
= GetUserH(argv
[1])))
3924 reply("MSG_NICK_UNKNOWN", argv
[1]);
3931 if(GetUserMode(channel
, invite
))
3933 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
3941 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
3942 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
3945 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
3947 irc_invite(chanserv
, invite
, channel
);
3949 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
3954 static CHANSERV_FUNC(cmd_inviteme
)
3956 if(GetUserMode(channel
, user
))
3958 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
3961 if(channel
->channel_info
3962 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
3964 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
3967 irc_invite(cmd
->parent
->bot
, user
, channel
);
3972 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
3975 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
3977 /* We display things based on two dimensions:
3978 * - Issue time: present or absent
3979 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3980 * (in order of precedence, so something both expired and revoked
3981 * only counts as revoked)
3983 combo
= (suspended
->issued
? 4 : 0)
3984 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
3986 case 0: /* no issue time, indefinite expiration */
3987 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
3989 case 1: /* no issue time, expires in future */
3990 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
3991 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
3993 case 2: /* no issue time, expired */
3994 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
3995 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
3997 case 3: /* no issue time, revoked */
3998 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
3999 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4001 case 4: /* issue time set, indefinite expiration */
4002 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4003 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4005 case 5: /* issue time set, expires in future */
4006 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4007 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4008 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4010 case 6: /* issue time set, expired */
4011 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4012 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4013 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4015 case 7: /* issue time set, revoked */
4016 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4017 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4018 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4021 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4026 static CHANSERV_FUNC(cmd_info
)
4028 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4029 struct userData
*uData
, *owner
;
4030 struct chanData
*cData
;
4031 struct do_not_register
*dnr
;
4036 cData
= channel
->channel_info
;
4037 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4040 uData
= GetChannelUser(cData
, user
->handle_info
);
4041 if(uData
&& (uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4043 mod_chanmode_format(&cData
->modes
, modes
);
4044 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4045 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4048 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4052 note
= iter_data(it
);
4053 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4056 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4057 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4060 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4061 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4062 if(owner
->access
== UL_OWNER
)
4063 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4064 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4065 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
4066 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4067 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4069 privileged
= IsStaff(user
);
4070 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4071 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4073 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4074 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4076 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4078 struct suspended
*suspended
;
4079 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4080 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4081 show_suspension_info(cmd
, user
, suspended
);
4083 else if(IsSuspended(cData
))
4085 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4086 show_suspension_info(cmd
, user
, cData
->suspended
);
4088 reply("CSMSG_CHANNEL_END");
4092 static CHANSERV_FUNC(cmd_netinfo
)
4094 extern time_t boot_time
;
4095 extern unsigned long burst_length
;
4096 char interval
[INTERVALLEN
];
4098 reply("CSMSG_NETWORK_INFO");
4099 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4100 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4101 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4102 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4103 reply("CSMSG_NETWORK_BANS", banCount
);
4104 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4105 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4106 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4111 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4113 struct helpfile_table table
;
4115 struct userNode
*user
;
4120 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4121 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4122 for(nn
=0; nn
<list
->used
; nn
++)
4124 user
= list
->list
[nn
];
4125 if(user
->modes
& skip_flags
)
4129 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4132 nick
= alloca(strlen(user
->nick
)+3);
4133 sprintf(nick
, "(%s)", user
->nick
);
4137 table
.contents
[table
.length
][0] = nick
;
4140 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4143 static CHANSERV_FUNC(cmd_ircops
)
4145 reply("CSMSG_STAFF_OPERS");
4146 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4150 static CHANSERV_FUNC(cmd_helpers
)
4152 reply("CSMSG_STAFF_HELPERS");
4153 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4157 static CHANSERV_FUNC(cmd_staff
)
4159 reply("CSMSG_NETWORK_STAFF");
4160 cmd_ircops(CSFUNC_ARGS
);
4161 cmd_helpers(CSFUNC_ARGS
);
4165 static CHANSERV_FUNC(cmd_peek
)
4167 struct modeNode
*mn
;
4168 char modes
[MODELEN
];
4170 struct helpfile_table table
;
4172 irc_make_chanmode(channel
, modes
);
4174 reply("CSMSG_PEEK_INFO", channel
->name
);
4176 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4177 reply("CSMSG_PEEK_MODES", modes
);
4178 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4182 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4183 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4184 for(n
= 0; n
< channel
->members
.used
; n
++)
4186 mn
= channel
->members
.list
[n
];
4187 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4189 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4190 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4195 reply("CSMSG_PEEK_OPS");
4196 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4199 reply("CSMSG_PEEK_NO_OPS");
4200 reply("CSMSG_PEEK_END");
4204 static MODCMD_FUNC(cmd_wipeinfo
)
4206 struct handle_info
*victim
;
4207 struct userData
*ud
, *actor
;
4210 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4211 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4213 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4215 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4218 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4220 reply("MSG_USER_OUTRANKED", victim
->handle
);
4226 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4230 static CHANSERV_FUNC(cmd_resync
)
4232 struct mod_chanmode
*changes
;
4233 struct chanData
*cData
= channel
->channel_info
;
4234 unsigned int ii
, used
;
4236 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4237 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4239 struct modeNode
*mn
= channel
->members
.list
[ii
];
4240 struct userData
*uData
;
4242 if(IsService(mn
->user
))
4245 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4246 if(!cData
->lvlOpts
[lvlGiveOps
]
4247 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4249 if(!(mn
->modes
& MODE_CHANOP
))
4251 changes
->args
[used
].mode
= MODE_CHANOP
;
4252 changes
->args
[used
++].u
.member
= mn
;
4255 else if(!cData
->lvlOpts
[lvlGiveHalfOps
]
4256 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
]))
4258 if(!(mn
->modes
& MODE_HALFOP
))
4260 changes
->args
[used
].mode
= MODE_HALFOP
;
4261 changes
->args
[used
++].u
.member
= mn
;
4263 if(mn
->modes
& MODE_CHANOP
)
4265 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4266 changes
->args
[used
++].u
.member
= mn
;
4268 if(mn
->modes
& MODE_VOICE
)
4270 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4271 changes
->args
[used
++].u
.member
= mn
;
4274 else if(!cData
->lvlOpts
[lvlGiveVoice
]
4275 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
]))
4277 if(mn
->modes
& MODE_CHANOP
)
4279 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4280 changes
->args
[used
++].u
.member
= mn
;
4282 if(mn
->modes
& MODE_HALFOP
)
4284 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4285 changes
->args
[used
++].u
.member
= mn
;
4287 if(!(mn
->modes
& MODE_VOICE
))
4289 changes
->args
[used
].mode
= MODE_VOICE
;
4290 changes
->args
[used
++].u
.member
= mn
;
4297 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4298 changes
->args
[used
++].u
.member
= mn
;
4302 changes
->argc
= used
;
4303 modcmd_chanmode_announce(changes
);
4304 mod_chanmode_free(changes
);
4305 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4309 static CHANSERV_FUNC(cmd_seen
)
4311 struct userData
*uData
;
4312 struct handle_info
*handle
;
4313 char seen
[INTERVALLEN
];
4317 if(!irccasecmp(argv
[1], chanserv
->nick
))
4319 reply("CSMSG_IS_CHANSERV");
4323 if(!(handle
= get_handle_info(argv
[1])))
4325 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4329 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4331 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4336 reply("CSMSG_USER_PRESENT", handle
->handle
);
4337 else if(uData
->seen
)
4338 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4340 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4342 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4343 reply("CSMSG_USER_VACATION", handle
->handle
);
4348 static MODCMD_FUNC(cmd_names
)
4350 struct userNode
*targ
;
4351 struct userData
*targData
;
4352 unsigned int ii
, pos
;
4355 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4357 targ
= channel
->members
.list
[ii
]->user
;
4358 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4361 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4364 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4368 if(IsUserSuspended(targData
))
4370 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4373 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4374 reply("CSMSG_END_NAMES", channel
->name
);
4379 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4381 switch(ntype
->visible_type
)
4383 case NOTE_VIS_ALL
: return 1;
4384 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4385 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4390 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4392 struct userData
*uData
;
4394 switch(ntype
->set_access_type
)
4396 case NOTE_SET_CHANNEL_ACCESS
:
4397 if(!user
->handle_info
)
4399 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4401 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4402 case NOTE_SET_CHANNEL_SETTER
:
4403 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4404 case NOTE_SET_PRIVILEGED
: default:
4405 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4409 static CHANSERV_FUNC(cmd_note
)
4411 struct chanData
*cData
;
4413 struct note_type
*ntype
;
4415 cData
= channel
->channel_info
;
4418 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4422 /* If no arguments, show all visible notes for the channel. */
4428 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4430 note
= iter_data(it
);
4431 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4434 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4435 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4438 reply("CSMSG_NOTELIST_END", channel
->name
);
4440 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4442 /* If one argument, show the named note. */
4445 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4446 && note_type_visible_to_user(cData
, note
->type
, user
))
4448 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4450 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4451 && note_type_visible_to_user(NULL
, ntype
, user
))
4453 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4458 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4462 /* Assume they're trying to set a note. */
4466 ntype
= dict_find(note_types
, argv
[1], NULL
);
4469 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4472 else if(note_type_settable_by_user(channel
, ntype
, user
))
4474 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4475 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4476 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4477 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4478 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4480 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4482 /* The note is viewable to staff only, so return 0
4483 to keep the invocation from getting logged (or
4484 regular users can see it in !events). */
4490 reply("CSMSG_NO_ACCESS");
4497 static CHANSERV_FUNC(cmd_delnote
)
4502 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4503 || !note_type_settable_by_user(channel
, note
->type
, user
))
4505 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4508 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4509 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4513 static CHANSERV_FUNC(cmd_events
)
4515 struct logSearch discrim
;
4516 struct logReport report
;
4517 unsigned int matches
, limit
;
4519 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4520 if(limit
< 1 || limit
> 200)
4523 memset(&discrim
, 0, sizeof(discrim
));
4524 discrim
.masks
.bot
= chanserv
;
4525 discrim
.masks
.channel_name
= channel
->name
;
4527 discrim
.masks
.command
= argv
[2];
4528 discrim
.limit
= limit
;
4529 discrim
.max_time
= INT_MAX
;
4530 discrim
.severities
= 1 << LOG_COMMAND
;
4531 report
.reporter
= chanserv
;
4533 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4535 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4537 reply("MSG_MATCH_COUNT", matches
);
4539 reply("MSG_NO_MATCHES");
4543 static CHANSERV_FUNC(cmd_say
)
4549 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4550 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4552 else if(GetUserH(argv
[1]))
4555 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4556 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4560 reply("MSG_NOT_TARGET_NAME");
4566 static CHANSERV_FUNC(cmd_emote
)
4572 /* CTCP is so annoying. */
4573 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4574 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4576 else if(GetUserH(argv
[1]))
4578 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4579 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4583 reply("MSG_NOT_TARGET_NAME");
4589 struct channelList
*
4590 chanserv_support_channels(void)
4592 return &chanserv_conf
.support_channels
;
4595 static CHANSERV_FUNC(cmd_expire
)
4597 int channel_count
= registered_channels
;
4598 expire_channels(NULL
);
4599 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4604 chanserv_expire_suspension(void *data
)
4606 struct suspended
*suspended
= data
;
4607 struct chanNode
*channel
;
4609 if(!suspended
->expires
|| (now
< suspended
->expires
))
4610 suspended
->revoked
= now
;
4611 channel
= suspended
->cData
->channel
;
4612 suspended
->cData
->channel
= channel
;
4613 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4614 if(!IsOffChannel(suspended
->cData
))
4616 struct mod_chanmode change
;
4617 mod_chanmode_init(&change
);
4619 change
.args
[0].mode
= MODE_CHANOP
;
4620 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4621 mod_chanmode_announce(chanserv
, channel
, &change
);
4625 static CHANSERV_FUNC(cmd_csuspend
)
4627 struct suspended
*suspended
;
4628 char reason
[MAXLEN
];
4629 time_t expiry
, duration
;
4630 struct userData
*uData
;
4634 if(IsProtected(channel
->channel_info
))
4636 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4640 if(argv
[1][0] == '!')
4642 else if(IsSuspended(channel
->channel_info
))
4644 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4645 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4649 if(!strcmp(argv
[1], "0"))
4651 else if((duration
= ParseInterval(argv
[1])))
4652 expiry
= now
+ duration
;
4655 reply("MSG_INVALID_DURATION", argv
[1]);
4659 unsplit_string(argv
+ 2, argc
- 2, reason
);
4661 suspended
= calloc(1, sizeof(*suspended
));
4662 suspended
->revoked
= 0;
4663 suspended
->issued
= now
;
4664 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4665 suspended
->expires
= expiry
;
4666 suspended
->reason
= strdup(reason
);
4667 suspended
->cData
= channel
->channel_info
;
4668 suspended
->previous
= suspended
->cData
->suspended
;
4669 suspended
->cData
->suspended
= suspended
;
4671 if(suspended
->expires
)
4672 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4674 if(IsSuspended(channel
->channel_info
))
4676 suspended
->previous
->revoked
= now
;
4677 if(suspended
->previous
->expires
)
4678 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4679 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4680 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4684 /* Mark all users in channel as absent. */
4685 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4694 /* Mark the channel as suspended, then part. */
4695 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4696 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4697 reply("CSMSG_SUSPENDED", channel
->name
);
4698 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4699 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4704 static CHANSERV_FUNC(cmd_cunsuspend
)
4706 struct suspended
*suspended
;
4707 char message
[MAXLEN
];
4709 if(!IsSuspended(channel
->channel_info
))
4711 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4715 suspended
= channel
->channel_info
->suspended
;
4717 /* Expire the suspension and join ChanServ to the channel. */
4718 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4719 chanserv_expire_suspension(suspended
);
4720 reply("CSMSG_UNSUSPENDED", channel
->name
);
4721 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4722 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4726 typedef struct chanservSearch
4734 unsigned long flags
;
4738 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4741 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4746 search
= malloc(sizeof(struct chanservSearch
));
4747 memset(search
, 0, sizeof(*search
));
4750 for(i
= 0; i
< argc
; i
++)
4752 /* Assume all criteria require arguments. */
4755 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4759 if(!irccasecmp(argv
[i
], "name"))
4760 search
->name
= argv
[++i
];
4761 else if(!irccasecmp(argv
[i
], "registrar"))
4762 search
->registrar
= argv
[++i
];
4763 else if(!irccasecmp(argv
[i
], "unvisited"))
4764 search
->unvisited
= ParseInterval(argv
[++i
]);
4765 else if(!irccasecmp(argv
[i
], "registered"))
4766 search
->registered
= ParseInterval(argv
[++i
]);
4767 else if(!irccasecmp(argv
[i
], "flags"))
4770 if(!irccasecmp(argv
[i
], "nodelete"))
4771 search
->flags
|= CHANNEL_NODELETE
;
4772 else if(!irccasecmp(argv
[i
], "suspended"))
4773 search
->flags
|= CHANNEL_SUSPENDED
;
4776 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4780 else if(!irccasecmp(argv
[i
], "limit"))
4781 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4784 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4789 if(search
->name
&& !strcmp(search
->name
, "*"))
4791 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4792 search
->registrar
= 0;
4801 chanserv_channel_match(struct chanData
*channel
, search_t search
)
4803 const char *name
= channel
->channel
->name
;
4804 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
4805 (search
->registrar
&& !channel
->registrar
) ||
4806 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
4807 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
4808 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
4809 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
4816 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
4818 struct chanData
*channel
;
4819 unsigned int matches
= 0;
4821 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
4823 if(!chanserv_channel_match(channel
, search
))
4833 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
4838 search_print(struct chanData
*channel
, void *data
)
4840 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
4843 static CHANSERV_FUNC(cmd_search
)
4846 unsigned int matches
;
4847 channel_search_func action
;
4851 if(!irccasecmp(argv
[1], "count"))
4852 action
= search_count
;
4853 else if(!irccasecmp(argv
[1], "print"))
4854 action
= search_print
;
4857 reply("CSMSG_ACTION_INVALID", argv
[1]);
4861 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
4865 if(action
== search_count
)
4866 search
->limit
= INT_MAX
;
4868 if(action
== search_print
)
4870 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4874 matches
= chanserv_channel_search(search
, action
, user
);
4877 reply("MSG_MATCH_COUNT", matches
);
4879 reply("MSG_NO_MATCHES");
4885 static CHANSERV_FUNC(cmd_unvisited
)
4887 struct chanData
*cData
;
4888 time_t interval
= chanserv_conf
.channel_expire_delay
;
4889 char buffer
[INTERVALLEN
];
4890 unsigned int limit
= 25, matches
= 0;
4894 interval
= ParseInterval(argv
[1]);
4896 limit
= atoi(argv
[2]);
4899 intervalString(buffer
, interval
, user
->handle_info
);
4900 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
4902 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
4904 if((now
- cData
->visited
) < interval
)
4907 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
4908 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
4915 static MODCMD_FUNC(chan_opt_defaulttopic
)
4921 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4923 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4927 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
4929 free(channel
->channel_info
->topic
);
4930 if(topic
[0] == '*' && topic
[1] == 0)
4932 topic
= channel
->channel_info
->topic
= NULL
;
4936 topic
= channel
->channel_info
->topic
= strdup(topic
);
4937 if(channel
->channel_info
->topic_mask
4938 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
4939 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4941 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
4944 if(channel
->channel_info
->topic
)
4945 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
4947 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
4951 static MODCMD_FUNC(chan_opt_topicmask
)
4955 struct chanData
*cData
= channel
->channel_info
;
4958 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4960 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4964 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
4966 if(cData
->topic_mask
)
4967 free(cData
->topic_mask
);
4968 if(mask
[0] == '*' && mask
[1] == 0)
4970 cData
->topic_mask
= 0;
4974 cData
->topic_mask
= strdup(mask
);
4976 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
4977 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
4978 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4982 if(channel
->channel_info
->topic_mask
)
4983 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
4985 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
4989 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
4993 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
4997 if(greeting
[0] == '*' && greeting
[1] == 0)
5001 unsigned int length
= strlen(greeting
);
5002 if(length
> chanserv_conf
.greeting_length
)
5004 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5007 *data
= strdup(greeting
);
5016 reply(name
, user_find_message(user
, "MSG_NONE"));
5020 static MODCMD_FUNC(chan_opt_greeting
)
5022 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5025 static MODCMD_FUNC(chan_opt_usergreeting
)
5027 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5030 static MODCMD_FUNC(chan_opt_modes
)
5032 struct mod_chanmode
*new_modes
;
5033 char modes
[MODELEN
];
5037 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5039 reply("CSMSG_NO_ACCESS");
5042 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5044 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5046 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5048 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5051 else if(new_modes
->argc
> 1)
5053 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5054 mod_chanmode_free(new_modes
);
5059 channel
->channel_info
->modes
= *new_modes
;
5060 modcmd_chanmode_announce(new_modes
);
5061 mod_chanmode_free(new_modes
);
5065 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5067 reply("CSMSG_SET_MODES", modes
);
5069 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5073 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5075 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5077 struct chanData
*cData
= channel
->channel_info
;
5082 /* Set flag according to value. */
5083 if(enabled_string(argv
[1]))
5085 cData
->flags
|= mask
;
5088 else if(disabled_string(argv
[1]))
5090 cData
->flags
&= ~mask
;
5095 reply("MSG_INVALID_BINARY", argv
[1]);
5101 /* Find current option value. */
5102 value
= (cData
->flags
& mask
) ? 1 : 0;
5106 reply(name
, user_find_message(user
, "MSG_ON"));
5108 reply(name
, user_find_message(user
, "MSG_OFF"));
5112 static MODCMD_FUNC(chan_opt_nodelete
)
5114 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5116 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5120 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5123 static MODCMD_FUNC(chan_opt_dynlimit
)
5125 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5128 static MODCMD_FUNC(chan_opt_offchannel
)
5130 struct chanData
*cData
= channel
->channel_info
;
5135 /* Set flag according to value. */
5136 if(enabled_string(argv
[1]))
5138 if(!IsOffChannel(cData
))
5139 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5140 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5143 else if(disabled_string(argv
[1]))
5145 if(IsOffChannel(cData
))
5147 struct mod_chanmode change
;
5148 mod_chanmode_init(&change
);
5150 change
.args
[0].mode
= MODE_CHANOP
;
5151 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5152 mod_chanmode_announce(chanserv
, channel
, &change
);
5154 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5159 reply("MSG_INVALID_BINARY", argv
[1]);
5165 /* Find current option value. */
5166 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5170 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5172 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5176 static MODCMD_FUNC(chan_opt_defaults
)
5178 struct userData
*uData
;
5179 struct chanData
*cData
;
5180 const char *confirm
;
5181 enum levelOption lvlOpt
;
5182 enum charOption chOpt
;
5184 cData
= channel
->channel_info
;
5185 uData
= GetChannelUser(cData
, user
->handle_info
);
5186 if(!uData
|| (uData
->access
< UL_OWNER
))
5188 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5191 confirm
= make_confirmation_string(uData
);
5192 if((argc
< 2) || strcmp(argv
[1], confirm
))
5194 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5197 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5198 cData
->modes
= chanserv_conf
.default_modes
;
5199 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5200 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5201 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5202 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5203 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5208 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5210 struct chanData
*cData
= channel
->channel_info
;
5211 struct userData
*uData
;
5212 unsigned short value
;
5216 if(!check_user_level(channel
, user
, option
, 1, 1))
5218 reply("CSMSG_CANNOT_SET");
5221 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5222 if(!value
&& strcmp(argv
[1], "0"))
5224 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5227 uData
= GetChannelUser(cData
, user
->handle_info
);
5228 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5230 reply("CSMSG_BAD_SETLEVEL");
5236 if(value
> cData
->lvlOpts
[lvlGiveOps
])
5238 reply("CSMSG_BAD_GIVEVOICE", cData
->lvlOpts
[lvlGiveOps
]);
5242 case lvlGiveHalfOps
:
5243 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5245 reply("CSMSG_BAD_GIVEHOPS", cData
->lvlOpts
[lvlGiveHalfOps
]);
5250 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5252 reply("CSMSG_BAD_GIVEOPS", cData
->lvlOpts
[lvlGiveVoice
]);
5257 /* This test only applies to owners, since non-owners
5258 * trying to set an option to above their level get caught
5259 * by the CSMSG_BAD_SETLEVEL test above.
5261 if(value
> uData
->access
)
5263 reply("CSMSG_BAD_SETTERS");
5270 cData
->lvlOpts
[option
] = value
;
5272 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5276 static MODCMD_FUNC(chan_opt_enfops
)
5278 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5281 static MODCMD_FUNC(chan_opt_enfhalfops
)
5283 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5286 static MODCMD_FUNC(chan_opt_giveops
)
5288 return channel_level_option(lvlGiveOps
, CSFUNC_ARGS
);
5291 static MODCMD_FUNC(chan_opt_givehalfops
)
5293 return channel_level_option(lvlGiveHalfOps
, CSFUNC_ARGS
);
5296 static MODCMD_FUNC(chan_opt_enfmodes
)
5298 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5301 static MODCMD_FUNC(chan_opt_enftopic
)
5303 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5306 static MODCMD_FUNC(chan_opt_pubcmd
)
5308 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5311 static MODCMD_FUNC(chan_opt_setters
)
5313 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5316 static MODCMD_FUNC(chan_opt_ctcpusers
)
5318 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5321 static MODCMD_FUNC(chan_opt_userinfo
)
5323 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5326 static MODCMD_FUNC(chan_opt_givevoice
)
5328 return channel_level_option(lvlGiveVoice
, CSFUNC_ARGS
);
5331 static MODCMD_FUNC(chan_opt_topicsnarf
)
5333 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5336 static MODCMD_FUNC(chan_opt_inviteme
)
5338 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5342 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5344 struct chanData
*cData
= channel
->channel_info
;
5345 int count
= charOptions
[option
].count
, index
;
5349 index
= atoi(argv
[1]);
5351 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5353 reply("CSMSG_INVALID_NUMERIC", index
);
5354 /* Show possible values. */
5355 for(index
= 0; index
< count
; index
++)
5356 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5360 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5364 /* Find current option value. */
5367 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5371 /* Somehow, the option value is corrupt; reset it to the default. */
5372 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5377 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5381 static MODCMD_FUNC(chan_opt_protect
)
5383 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5386 static MODCMD_FUNC(chan_opt_toys
)
5388 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5391 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5393 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5396 static MODCMD_FUNC(chan_opt_topicrefresh
)
5398 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5401 static struct svccmd_list set_shows_list
;
5404 handle_svccmd_unbind(struct svccmd
*target
) {
5406 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5407 if(target
== set_shows_list
.list
[ii
])
5408 set_shows_list
.used
= 0;
5411 static CHANSERV_FUNC(cmd_set
)
5413 struct svccmd
*subcmd
;
5417 /* Check if we need to (re-)initialize set_shows_list. */
5418 if(!set_shows_list
.used
)
5420 if(!set_shows_list
.size
)
5422 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5423 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5425 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5427 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5428 sprintf(buf
, "%s %s", argv
[0], name
);
5429 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5432 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5435 svccmd_list_append(&set_shows_list
, subcmd
);
5441 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5443 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5445 subcmd
= set_shows_list
.list
[ii
];
5446 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5448 reply("CSMSG_CHANNEL_OPTIONS_END");
5452 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5453 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5456 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5459 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5461 reply("CSMSG_NO_ACCESS");
5465 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5469 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5471 struct userData
*uData
;
5473 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5476 reply("CSMSG_NOT_USER", channel
->name
);
5482 /* Just show current option value. */
5484 else if(enabled_string(argv
[1]))
5486 uData
->flags
|= mask
;
5488 else if(disabled_string(argv
[1]))
5490 uData
->flags
&= ~mask
;
5494 reply("MSG_INVALID_BINARY", argv
[1]);
5498 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5502 static MODCMD_FUNC(user_opt_noautoop
)
5504 struct userData
*uData
;
5506 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5509 reply("CSMSG_NOT_USER", channel
->name
);
5512 if(uData
->access
< channel
->channel_info
->lvlOpts
[lvlGiveOps
])
5513 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5515 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5518 static MODCMD_FUNC(user_opt_autoinvite
)
5520 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5523 static MODCMD_FUNC(user_opt_info
)
5525 struct userData
*uData
;
5528 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5532 /* If they got past the command restrictions (which require access)
5533 * but fail this test, we have some fool with security override on.
5535 reply("CSMSG_NOT_USER", channel
->name
);
5542 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5543 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5545 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5548 bp
= strcspn(infoline
, "\001");
5551 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5556 if(infoline
[0] == '*' && infoline
[1] == 0)
5559 uData
->info
= strdup(infoline
);
5562 reply("CSMSG_USET_INFO", uData
->info
);
5564 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5568 struct svccmd_list uset_shows_list
;
5570 static CHANSERV_FUNC(cmd_uset
)
5572 struct svccmd
*subcmd
;
5576 /* Check if we need to (re-)initialize uset_shows_list. */
5577 if(!uset_shows_list
.used
)
5581 "NoAutoOp", "AutoInvite", "Info"
5584 if(!uset_shows_list
.size
)
5586 uset_shows_list
.size
= ArrayLength(options
);
5587 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5589 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5591 const char *name
= options
[ii
];
5592 sprintf(buf
, "%s %s", argv
[0], name
);
5593 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5596 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5599 svccmd_list_append(&uset_shows_list
, subcmd
);
5605 /* Do this so options are presented in a consistent order. */
5606 reply("CSMSG_USER_OPTIONS");
5607 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5608 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5612 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5613 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5616 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5620 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5623 static CHANSERV_FUNC(cmd_giveownership
)
5625 struct handle_info
*new_owner_hi
;
5626 struct userData
*new_owner
, *curr_user
;
5627 struct chanData
*cData
= channel
->channel_info
;
5628 struct do_not_register
*dnr
;
5630 unsigned short co_access
;
5631 char reason
[MAXLEN
];
5634 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5635 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5636 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5638 struct userData
*owner
= NULL
;
5639 for(curr_user
= channel
->channel_info
->users
;
5641 curr_user
= curr_user
->next
)
5643 if(curr_user
->access
!= UL_OWNER
)
5647 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5654 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5656 char delay
[INTERVALLEN
];
5657 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5658 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5661 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5663 if(new_owner_hi
== user
->handle_info
)
5665 reply("CSMSG_NO_TRANSFER_SELF");
5668 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5673 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5677 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5681 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5683 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5686 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5687 if(!IsHelping(user
))
5688 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5690 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5693 if(new_owner
->access
>= UL_COOWNER
)
5694 co_access
= new_owner
->access
;
5696 co_access
= UL_COOWNER
;
5697 new_owner
->access
= UL_OWNER
;
5699 curr_user
->access
= co_access
;
5700 cData
->ownerTransfer
= now
;
5701 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5702 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5703 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5707 static CHANSERV_FUNC(cmd_suspend
)
5709 struct handle_info
*hi
;
5710 struct userData
*self
, *target
;
5713 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5714 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5715 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5717 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5720 if(target
->access
>= self
->access
)
5722 reply("MSG_USER_OUTRANKED", hi
->handle
);
5725 if(target
->flags
& USER_SUSPENDED
)
5727 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5732 target
->present
= 0;
5735 target
->flags
|= USER_SUSPENDED
;
5736 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5740 static CHANSERV_FUNC(cmd_unsuspend
)
5742 struct handle_info
*hi
;
5743 struct userData
*self
, *target
;
5746 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5747 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5748 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5750 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5753 if(target
->access
>= self
->access
)
5755 reply("MSG_USER_OUTRANKED", hi
->handle
);
5758 if(!(target
->flags
& USER_SUSPENDED
))
5760 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5763 target
->flags
&= ~USER_SUSPENDED
;
5764 scan_user_presence(target
, NULL
);
5765 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5769 static MODCMD_FUNC(cmd_deleteme
)
5771 struct handle_info
*hi
;
5772 struct userData
*target
;
5773 const char *confirm_string
;
5774 unsigned short access
;
5777 hi
= user
->handle_info
;
5778 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5780 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5783 if(target
->access
== UL_OWNER
)
5785 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5788 confirm_string
= make_confirmation_string(target
);
5789 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
5791 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
5794 access
= target
->access
;
5795 channel_name
= strdup(channel
->name
);
5796 del_channel_user(target
, 1);
5797 reply("CSMSG_DELETED_YOU", access
, channel_name
);
5803 chanserv_refresh_topics(UNUSED_ARG(void *data
))
5805 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
5806 struct chanData
*cData
;
5809 for(cData
= channelList
; cData
; cData
= cData
->next
)
5811 if(IsSuspended(cData
))
5813 opt
= cData
->chOpts
[chTopicRefresh
];
5816 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
5819 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
5820 cData
->last_refresh
= refresh_num
;
5822 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
5825 static CHANSERV_FUNC(cmd_unf
)
5829 char response
[MAXLEN
];
5830 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
5831 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5832 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5835 reply("CSMSG_UNF_RESPONSE");
5839 static CHANSERV_FUNC(cmd_ping
)
5843 char response
[MAXLEN
];
5844 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
5845 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5846 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5849 reply("CSMSG_PING_RESPONSE");
5853 static CHANSERV_FUNC(cmd_wut
)
5857 char response
[MAXLEN
];
5858 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
5859 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5860 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5863 reply("CSMSG_WUT_RESPONSE");
5867 static CHANSERV_FUNC(cmd_8ball
)
5869 unsigned int i
, j
, accum
;
5874 for(i
=1; i
<argc
; i
++)
5875 for(j
=0; argv
[i
][j
]; j
++)
5876 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
5877 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
5880 char response
[MAXLEN
];
5881 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
5882 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5885 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
5889 static CHANSERV_FUNC(cmd_d
)
5891 unsigned long sides
, count
, modifier
, ii
, total
;
5892 char response
[MAXLEN
], *sep
;
5896 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
5906 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
5907 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
5911 else if((sep
[0] == '-') && isdigit(sep
[1]))
5912 modifier
= strtoul(sep
, NULL
, 10);
5913 else if((sep
[0] == '+') && isdigit(sep
[1]))
5914 modifier
= strtoul(sep
+1, NULL
, 10);
5921 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
5926 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
5929 for(total
= ii
= 0; ii
< count
; ++ii
)
5930 total
+= (rand() % sides
) + 1;
5933 if((count
> 1) || modifier
)
5935 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
5936 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
5940 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
5941 sprintf(response
, fmt
, total
, sides
);
5944 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5946 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5950 static CHANSERV_FUNC(cmd_huggle
)
5952 /* CTCP must be via PRIVMSG, never notice */
5954 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
5956 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
5960 static CHANSERV_FUNC(cmd_calc
)
5962 char response
[MAXLEN
];
5965 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
5968 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5970 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5975 chanserv_adjust_limit(void *data
)
5977 struct mod_chanmode change
;
5978 struct chanData
*cData
= data
;
5979 struct chanNode
*channel
= cData
->channel
;
5982 if(IsSuspended(cData
))
5985 cData
->limitAdjusted
= now
;
5986 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
5987 if(cData
->modes
.modes_set
& MODE_LIMIT
)
5989 if(limit
> cData
->modes
.new_limit
)
5990 limit
= cData
->modes
.new_limit
;
5991 else if(limit
== cData
->modes
.new_limit
)
5995 mod_chanmode_init(&change
);
5996 change
.modes_set
= MODE_LIMIT
;
5997 change
.new_limit
= limit
;
5998 mod_chanmode_announce(chanserv
, channel
, &change
);
6002 handle_new_channel(struct chanNode
*channel
)
6004 struct chanData
*cData
;
6006 if(!(cData
= channel
->channel_info
))
6009 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6010 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6012 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6013 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6016 /* Welcome to my worst nightmare. Warning: Read (or modify)
6017 the code below at your own risk. */
6019 handle_join(struct modeNode
*mNode
)
6021 struct mod_chanmode change
;
6022 struct userNode
*user
= mNode
->user
;
6023 struct chanNode
*channel
= mNode
->channel
;
6024 struct chanData
*cData
;
6025 struct userData
*uData
= NULL
;
6026 struct banData
*bData
;
6027 struct handle_info
*handle
;
6028 unsigned int modes
= 0, info
= 0;
6031 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6034 cData
= channel
->channel_info
;
6035 if(channel
->members
.used
> cData
->max
)
6036 cData
->max
= channel
->members
.used
;
6038 /* Check for bans. If they're joining through a ban, one of two
6040 * 1: Join during a netburst, by riding the break. Kick them
6041 * unless they have ops or voice in the channel.
6042 * 2: They're allowed to join through the ban (an invite in
6043 * ircu2.10, or a +e on Hybrid, or something).
6044 * If they're not joining through a ban, and the banlist is not
6045 * full, see if they're on the banlist for the channel. If so,
6048 /* This is really, really stupid. not all banned people are kicked.
6049 * sometimes we like to leave them unkicked.
6050 * I tried to explain this to the srvx developers and
6051 * got insulted.. hence one reason for this fork.
6053 if(user->uplink->burst && !mNode->modes)
6056 for(ii = 0; ii < channel->banlist.used; ii++)
6058 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6060 ** Riding a netburst. Naughty. **
6061 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6068 mod_chanmode_init(&change
);
6070 if(channel
->banlist
.used
< MAXBANS
)
6072 /* Not joining through a ban. */
6073 for(bData
= cData
->bans
;
6074 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6075 bData
= bData
->next
);
6079 char kick_reason
[MAXLEN
];
6080 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6082 bData
->triggered
= now
;
6083 if(bData
!= cData
->bans
)
6085 /* Shuffle the ban to the head of the list. */
6087 bData
->next
->prev
= bData
->prev
;
6089 bData
->prev
->next
= bData
->next
;
6092 bData
->next
= cData
->bans
;
6095 cData
->bans
->prev
= bData
;
6096 cData
->bans
= bData
;
6099 change
.args
[0].mode
= MODE_BAN
;
6100 change
.args
[0].u
.hostmask
= bData
->mask
;
6101 mod_chanmode_announce(chanserv
, channel
, &change
);
6102 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6107 /* ChanServ will not modify the limits in join-flooded channels.
6108 It will also skip DynLimit processing when the user (or srvx)
6109 is bursting in, because there are likely more incoming. */
6110 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6111 && !user
->uplink
->burst
6112 && !channel
->join_flooded
6113 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6115 /* The user count has begun "bumping" into the channel limit,
6116 so set a timer to raise the limit a bit. Any previous
6117 timers are removed so three incoming users within the delay
6118 results in one limit change, not three. */
6120 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6121 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6124 if(channel
->join_flooded
)
6126 /* don't automatically give ops or voice during a join flood */
6128 else if(cData
->lvlOpts
[lvlGiveOps
] == 0)
6129 modes
|= MODE_CHANOP
;
6130 else if(cData
->lvlOpts
[lvlGiveHalfOps
] == 0)
6131 modes
|= MODE_HALFOP
;
6132 else if(cData
->lvlOpts
[lvlGiveVoice
] == 0)
6133 modes
|= MODE_VOICE
;
6135 greeting
= cData
->greeting
;
6136 if(user
->handle_info
)
6138 handle
= user
->handle_info
;
6140 if(IsHelper(user
) && !IsHelping(user
))
6143 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6145 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6147 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6153 uData
= GetTrueChannelAccess(cData
, handle
);
6154 if(uData
&& !IsUserSuspended(uData
))
6156 /* Ops and above were handled by the above case. */
6157 if(IsUserAutoOp(uData
))
6159 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
6160 modes
|= MODE_CHANOP
;
6161 if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
6162 modes
|= MODE_HALFOP
;
6163 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
6164 modes
|= MODE_VOICE
;
6166 if(uData
->access
>= UL_PRESENT
)
6167 cData
->visited
= now
;
6168 if(cData
->user_greeting
)
6169 greeting
= cData
->user_greeting
;
6171 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6172 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6179 if(!user
->uplink
->burst
)
6183 if(modes
& MODE_CHANOP
) {
6184 modes
&= ~MODE_HALFOP
;
6185 modes
&= ~MODE_VOICE
;
6187 change
.args
[0].mode
= modes
;
6188 change
.args
[0].u
.member
= mNode
;
6189 mod_chanmode_announce(chanserv
, channel
, &change
);
6191 if(greeting
&& !user
->uplink
->burst
)
6192 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6194 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6200 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6202 struct mod_chanmode change
;
6203 struct userData
*channel
;
6204 unsigned int ii
, jj
;
6206 if(!user
->handle_info
)
6209 mod_chanmode_init(&change
);
6211 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6213 struct chanNode
*cn
;
6214 struct modeNode
*mn
;
6215 if(IsUserSuspended(channel
)
6216 || IsSuspended(channel
->channel
)
6217 || !(cn
= channel
->channel
->channel
))
6220 mn
= GetUserMode(cn
, user
);
6223 if(!IsUserSuspended(channel
)
6224 && IsUserAutoInvite(channel
)
6225 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6227 && !user
->uplink
->burst
)
6228 irc_invite(chanserv
, user
, cn
);
6232 if(channel
->access
>= UL_PRESENT
)
6233 channel
->channel
->visited
= now
;
6235 if(IsUserAutoOp(channel
))
6237 if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveOps
])
6238 change
.args
[0].mode
= MODE_CHANOP
;
6239 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
6240 change
.args
[0].mode
= MODE_HALFOP
;
6241 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveVoice
])
6242 change
.args
[0].mode
= MODE_VOICE
;
6244 change
.args
[0].mode
= 0;
6245 change
.args
[0].u
.member
= mn
;
6246 if(change
.args
[0].mode
)
6247 mod_chanmode_announce(chanserv
, cn
, &change
);
6250 channel
->seen
= now
;
6251 channel
->present
= 1;
6254 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6256 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6257 struct banData
*ban
;
6259 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6260 || !channel
->channel_info
6261 || IsSuspended(channel
->channel_info
))
6263 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6264 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6266 if(jj
< channel
->banlist
.used
)
6268 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6270 char kick_reason
[MAXLEN
];
6271 if(!user_matches_glob(user
, ban
->mask
, 1))
6273 change
.args
[0].mode
= MODE_BAN
;
6274 change
.args
[0].u
.hostmask
= ban
->mask
;
6275 mod_chanmode_announce(chanserv
, channel
, &change
);
6276 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6277 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6278 ban
->triggered
= now
;
6283 if(IsSupportHelper(user
))
6285 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6287 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6289 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6297 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6299 struct chanData
*cData
;
6300 struct userData
*uData
;
6302 cData
= mn
->channel
->channel_info
;
6303 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6306 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6308 /* Allow for a bit of padding so that the limit doesn't
6309 track the user count exactly, which could get annoying. */
6310 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6312 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6313 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6317 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6319 scan_user_presence(uData
, mn
->user
);
6323 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6325 unsigned int ii
, jj
;
6326 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6328 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6329 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6331 if(jj
< mn
->user
->channels
.used
)
6334 if(ii
== chanserv_conf
.support_channels
.used
)
6335 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6340 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6342 struct userData
*uData
;
6344 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6345 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6346 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6349 if(protect_user(victim
, kicker
, channel
->channel_info
))
6351 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6352 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6355 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6360 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6362 struct chanData
*cData
;
6364 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6367 cData
= channel
->channel_info
;
6368 if(bad_topic(channel
, user
, channel
->topic
))
6370 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6371 if(cData
->topic_mask
&& match_ircglob(old_topic
, cData
->topic_mask
))
6372 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6373 else if(cData
->topic
)
6374 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
6377 /* With topicsnarf, grab the topic and save it as the default topic. */
6378 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6381 cData
->topic
= strdup(channel
->topic
);
6387 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6389 struct mod_chanmode
*bounce
= NULL
;
6390 unsigned int bnc
, ii
;
6393 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6396 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6397 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6399 char correct
[MAXLEN
];
6400 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6401 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6402 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6404 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6406 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6408 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6409 if(!protect_user(victim
, user
, channel
->channel_info
))
6412 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6415 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6416 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6417 if(bounce
->args
[bnc
].u
.member
)
6421 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6422 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6424 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6426 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6428 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6429 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6432 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6433 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6434 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6437 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6439 const char *ban
= change
->args
[ii
].u
.hostmask
;
6440 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6443 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6444 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6445 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6447 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6452 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6453 mod_chanmode_announce(chanserv
, channel
, bounce
);
6454 for(ii
= 0; ii
< change
->argc
; ++ii
)
6455 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6456 free((char*)bounce
->args
[ii
].u
.hostmask
);
6457 mod_chanmode_free(bounce
);
6462 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6464 struct chanNode
*channel
;
6465 struct banData
*bData
;
6466 struct mod_chanmode change
;
6467 unsigned int ii
, jj
;
6468 char kick_reason
[MAXLEN
];
6470 mod_chanmode_init(&change
);
6472 change
.args
[0].mode
= MODE_BAN
;
6473 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6475 channel
= user
->channels
.list
[ii
]->channel
;
6476 /* Need not check for bans if they're opped or voiced. */
6477 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6479 /* Need not check for bans unless channel registration is active. */
6480 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6482 /* Look for a matching ban already on the channel. */
6483 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6484 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6486 /* Need not act if we found one. */
6487 if(jj
< channel
->banlist
.used
)
6489 /* Look for a matching ban in this channel. */
6490 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6492 if(!user_matches_glob(user
, bData
->mask
, 1))
6494 change
.args
[0].u
.hostmask
= bData
->mask
;
6495 mod_chanmode_announce(chanserv
, channel
, &change
);
6496 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6497 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6498 bData
->triggered
= now
;
6499 break; /* we don't need to check any more bans in the channel */
6504 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6506 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6510 dict_remove2(handle_dnrs
, old_handle
, 1);
6511 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6512 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6517 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6519 struct userNode
*h_user
;
6521 if(handle
->channels
)
6523 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6524 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6526 while(handle
->channels
)
6527 del_channel_user(handle
->channels
, 1);
6532 handle_server_link(UNUSED_ARG(struct server
*server
))
6534 struct chanData
*cData
;
6536 for(cData
= channelList
; cData
; cData
= cData
->next
)
6538 if(!IsSuspended(cData
))
6539 cData
->may_opchan
= 1;
6540 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6541 && !cData
->channel
->join_flooded
6542 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6543 < chanserv_conf
.adjust_threshold
))
6545 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6546 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6552 chanserv_conf_read(void)
6556 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6557 struct mod_chanmode
*change
;
6558 struct string_list
*strlist
;
6559 struct chanNode
*chan
;
6562 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6564 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6567 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6568 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6569 chanserv_conf
.support_channels
.used
= 0;
6570 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6572 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6574 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6577 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6579 channelList_append(&chanserv_conf
.support_channels
, chan
);
6582 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6585 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6588 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6590 channelList_append(&chanserv_conf
.support_channels
, chan
);
6592 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6593 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6594 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6595 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6596 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6597 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6598 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6599 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6600 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6601 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6602 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6603 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6604 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6605 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6606 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6607 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6608 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6609 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6610 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6611 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6612 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6613 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6614 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6616 NickChange(chanserv
, str
, 0);
6617 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6618 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6619 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6620 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6621 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6622 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6623 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6624 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6625 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6626 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6627 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6628 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6629 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6630 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6631 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6632 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6633 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6636 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6637 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6638 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6640 chanserv_conf
.default_modes
= *change
;
6641 mod_chanmode_free(change
);
6643 free_string_list(chanserv_conf
.set_shows
);
6644 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6646 strlist
= string_list_copy(strlist
);
6649 static const char *list
[] = {
6650 /* free form text */
6651 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6652 /* options based on user level */
6653 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveHalfOps", "GiveOps", "EnfOps",
6654 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6655 /* multiple choice options */
6656 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6657 /* binary options */
6658 "DynLimit", "NoDelete",
6663 strlist
= alloc_string_list(ArrayLength(list
)-1);
6664 for(ii
=0; list
[ii
]; ii
++)
6665 string_list_append(strlist
, strdup(list
[ii
]));
6667 chanserv_conf
.set_shows
= strlist
;
6668 /* We don't look things up now, in case the list refers to options
6669 * defined by modules initialized after this point. Just mark the
6670 * function list as invalid, so it will be initialized.
6672 set_shows_list
.used
= 0;
6673 free_string_list(chanserv_conf
.eightball
);
6674 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6677 strlist
= string_list_copy(strlist
);
6681 strlist
= alloc_string_list(4);
6682 string_list_append(strlist
, strdup("Yes."));
6683 string_list_append(strlist
, strdup("No."));
6684 string_list_append(strlist
, strdup("Maybe so."));
6686 chanserv_conf
.eightball
= strlist
;
6687 free_string_list(chanserv_conf
.old_ban_names
);
6688 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6690 strlist
= string_list_copy(strlist
);
6692 strlist
= alloc_string_list(2);
6693 chanserv_conf
.old_ban_names
= strlist
;
6694 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6695 off_channel
= str
? atoi(str
) : 0;
6699 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6702 struct note_type
*ntype
;
6705 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6707 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6710 if(!(ntype
= chanserv_create_note_type(key
)))
6712 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6716 /* Figure out set access */
6717 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6719 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6720 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6722 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6724 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6725 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6727 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6729 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6733 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6734 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6735 ntype
->set_access
.min_opserv
= 0;
6738 /* Figure out visibility */
6739 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6740 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6741 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6742 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6743 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6744 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6745 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6746 ntype
->visible_type
= NOTE_VIS_ALL
;
6748 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6750 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6751 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6755 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6757 struct handle_info
*handle
;
6758 struct userData
*uData
;
6759 char *seen
, *inf
, *flags
;
6761 unsigned short access
;
6763 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6765 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6769 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6770 if(access
> UL_OWNER
)
6772 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6776 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
6777 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
6778 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
6779 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
6780 handle
= get_handle_info(key
);
6783 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
6787 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
6788 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
6792 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6794 struct banData
*bData
;
6795 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
6796 time_t set_time
, triggered_time
, expires_time
;
6798 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6800 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
6804 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
6805 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
6806 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
6807 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
6808 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
6809 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
6810 if (!reason
|| !owner
)
6813 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
6814 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
6816 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
6818 expires_time
= set_time
+ atoi(s_duration
);
6822 if(!reason
|| (expires_time
&& (expires_time
< now
)))
6825 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
6828 static struct suspended
*
6829 chanserv_read_suspended(dict_t obj
)
6831 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
6835 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
6836 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6837 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
6838 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6839 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
6840 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6841 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
6842 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
6843 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
6844 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
6849 chanserv_channel_read(const char *key
, struct record_data
*hir
)
6851 struct suspended
*suspended
;
6852 struct mod_chanmode
*modes
;
6853 struct chanNode
*cNode
;
6854 struct chanData
*cData
;
6855 struct dict
*channel
, *obj
;
6856 char *str
, *argv
[10];
6860 channel
= hir
->d
.object
;
6862 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
6865 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
6868 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
6871 cData
= register_channel(cNode
, str
);
6874 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
6878 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
6880 enum levelOption lvlOpt
;
6881 enum charOption chOpt
;
6883 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
6884 cData
->flags
= atoi(str
);
6886 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6888 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
6890 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
6891 else if(levelOptions
[lvlOpt
].old_flag
)
6893 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6894 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
6896 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6900 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6902 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
6904 cData
->chOpts
[chOpt
] = str
[0];
6907 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
6909 enum levelOption lvlOpt
;
6910 enum charOption chOpt
;
6913 cData
->flags
= base64toint(str
, 5);
6914 count
= strlen(str
+= 5);
6915 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6918 if(levelOptions
[lvlOpt
].old_flag
)
6920 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6921 lvl
= levelOptions
[lvlOpt
].flag_value
;
6923 lvl
= levelOptions
[lvlOpt
].default_value
;
6925 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
6927 case 'c': lvl
= UL_COOWNER
; break;
6928 case 'm': lvl
= UL_MANAGER
; break;
6929 case 'n': lvl
= UL_OWNER
+1; break;
6930 case 'o': lvl
= UL_OP
; break;
6931 case 'p': lvl
= UL_PEON
; break;
6932 case 'h': lvl
= UL_HALFOP
; break;
6933 case 'w': lvl
= UL_OWNER
; break;
6934 default: lvl
= 0; break;
6936 cData
->lvlOpts
[lvlOpt
] = lvl
;
6938 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6939 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
6942 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
6944 suspended
= chanserv_read_suspended(obj
);
6945 cData
->suspended
= suspended
;
6946 suspended
->cData
= cData
;
6947 /* We could use suspended->expires and suspended->revoked to
6948 * set the CHANNEL_SUSPENDED flag, but we don't. */
6950 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
6952 suspended
= calloc(1, sizeof(*suspended
));
6953 suspended
->issued
= 0;
6954 suspended
->revoked
= 0;
6955 suspended
->suspender
= strdup(str
);
6956 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
6957 suspended
->expires
= str
? atoi(str
) : 0;
6958 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
6959 suspended
->reason
= strdup(str
? str
: "No reason");
6960 suspended
->previous
= NULL
;
6961 cData
->suspended
= suspended
;
6962 suspended
->cData
= cData
;
6966 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6967 suspended
= NULL
; /* to squelch a warning */
6970 if(IsSuspended(cData
)) {
6971 if(suspended
->expires
> now
)
6972 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6973 else if(suspended
->expires
)
6974 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6977 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
6978 struct mod_chanmode change
;
6979 mod_chanmode_init(&change
);
6981 change
.args
[0].mode
= MODE_CHANOP
;
6982 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
6983 mod_chanmode_announce(chanserv
, cNode
, &change
);
6986 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
6987 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6988 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
6989 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6990 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
6991 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6992 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
6993 cData
->max
= str
? atoi(str
) : 0;
6994 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
6995 cData
->greeting
= str
? strdup(str
) : NULL
;
6996 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
6997 cData
->user_greeting
= str
? strdup(str
) : NULL
;
6998 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
6999 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7000 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7001 cData
->topic
= str
? strdup(str
) : NULL
;
7003 if(!IsSuspended(cData
)
7004 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7005 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7006 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7007 cData
->modes
= *modes
;
7009 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7010 if(cData
->modes
.argc
> 1)
7011 cData
->modes
.argc
= 1;
7012 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7013 mod_chanmode_free(modes
);
7016 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7017 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7018 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7020 if(!cData
->users
&& !IsProtected(cData
))
7022 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7023 unregister_channel(cData
, "has empty user list.");
7027 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7028 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7029 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7031 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7032 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7034 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7035 struct record_data
*rd
= iter_data(it
);
7036 const char *note
, *setter
;
7038 if(rd
->type
!= RECDB_OBJECT
)
7040 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7044 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7046 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7048 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7052 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7053 if(!setter
) setter
= "<unknown>";
7054 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7062 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7064 const char *setter
, *reason
, *str
;
7065 struct do_not_register
*dnr
;
7067 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7070 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7073 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7076 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7079 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7082 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7084 dnr
->set
= atoi(str
);
7090 chanserv_saxdb_read(struct dict
*database
)
7092 struct dict
*section
;
7095 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7096 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7097 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7099 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7100 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7101 chanserv_channel_read(iter_key(it
), iter_data(it
));
7103 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7104 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7105 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7111 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7113 int high_present
= 0;
7114 saxdb_start_record(ctx
, KEY_USERS
, 1);
7115 for(; uData
; uData
= uData
->next
)
7117 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7119 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7120 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7121 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7123 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7125 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7126 saxdb_end_record(ctx
);
7128 saxdb_end_record(ctx
);
7129 return high_present
;
7133 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7137 saxdb_start_record(ctx
, KEY_BANS
, 1);
7138 for(; bData
; bData
= bData
->next
)
7140 saxdb_start_record(ctx
, bData
->mask
, 0);
7141 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7142 if(bData
->triggered
)
7143 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7145 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7147 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7149 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7150 saxdb_end_record(ctx
);
7152 saxdb_end_record(ctx
);
7156 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7158 saxdb_start_record(ctx
, name
, 0);
7159 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7160 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7162 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7164 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7166 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7168 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7169 saxdb_end_record(ctx
);
7173 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7177 enum levelOption lvlOpt
;
7178 enum charOption chOpt
;
7180 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7182 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7183 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7185 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7186 if(channel
->registrar
)
7187 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7188 if(channel
->greeting
)
7189 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7190 if(channel
->user_greeting
)
7191 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7192 if(channel
->topic_mask
)
7193 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7194 if(channel
->suspended
)
7195 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7197 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7198 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7199 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7200 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7201 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7203 buf
[0] = channel
->chOpts
[chOpt
];
7205 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7207 saxdb_end_record(ctx
);
7209 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7211 mod_chanmode_format(&channel
->modes
, buf
);
7212 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7215 high_present
= chanserv_write_users(ctx
, channel
->users
);
7216 chanserv_write_bans(ctx
, channel
->bans
);
7218 if(dict_size(channel
->notes
))
7222 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7223 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7225 struct note
*note
= iter_data(it
);
7226 saxdb_start_record(ctx
, iter_key(it
), 0);
7227 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7228 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7229 saxdb_end_record(ctx
);
7231 saxdb_end_record(ctx
);
7234 if(channel
->ownerTransfer
)
7235 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7236 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7237 saxdb_end_record(ctx
);
7241 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7245 saxdb_start_record(ctx
, ntype
->name
, 0);
7246 switch(ntype
->set_access_type
)
7248 case NOTE_SET_CHANNEL_ACCESS
:
7249 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7251 case NOTE_SET_CHANNEL_SETTER
:
7252 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7254 case NOTE_SET_PRIVILEGED
: default:
7255 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7258 switch(ntype
->visible_type
)
7260 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7261 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7262 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7264 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7265 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7266 saxdb_end_record(ctx
);
7270 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7272 struct do_not_register
*dnr
;
7275 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7277 dnr
= iter_data(it
);
7278 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7280 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7281 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7282 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7283 saxdb_end_record(ctx
);
7288 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7291 struct chanData
*channel
;
7294 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7295 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7296 chanserv_write_note_type(ctx
, iter_data(it
));
7297 saxdb_end_record(ctx
);
7300 saxdb_start_record(ctx
, KEY_DNR
, 1);
7301 write_dnrs_helper(ctx
, handle_dnrs
);
7302 write_dnrs_helper(ctx
, plain_dnrs
);
7303 write_dnrs_helper(ctx
, mask_dnrs
);
7304 saxdb_end_record(ctx
);
7307 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7308 for(channel
= channelList
; channel
; channel
= channel
->next
)
7309 chanserv_write_channel(ctx
, channel
);
7310 saxdb_end_record(ctx
);
7316 chanserv_db_cleanup(void) {
7318 unreg_part_func(handle_part
);
7320 unregister_channel(channelList
, "terminating.");
7321 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7322 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7323 free(chanserv_conf
.support_channels
.list
);
7324 dict_delete(handle_dnrs
);
7325 dict_delete(plain_dnrs
);
7326 dict_delete(mask_dnrs
);
7327 dict_delete(note_types
);
7328 free_string_list(chanserv_conf
.eightball
);
7329 free_string_list(chanserv_conf
.old_ban_names
);
7330 free_string_list(chanserv_conf
.set_shows
);
7331 free(set_shows_list
.list
);
7332 free(uset_shows_list
.list
);
7335 struct userData
*helper
= helperList
;
7336 helperList
= helperList
->next
;
7341 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7342 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7343 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7346 init_chanserv(const char *nick
)
7348 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7349 conf_register_reload(chanserv_conf_read
);
7351 reg_server_link_func(handle_server_link
);
7353 reg_new_channel_func(handle_new_channel
);
7354 reg_join_func(handle_join
);
7355 reg_part_func(handle_part
);
7356 reg_kick_func(handle_kick
);
7357 reg_topic_func(handle_topic
);
7358 reg_mode_change_func(handle_mode
);
7359 reg_nick_change_func(handle_nick_change
);
7361 reg_auth_func(handle_auth
);
7362 reg_handle_rename_func(handle_rename
);
7363 reg_unreg_func(handle_unreg
);
7365 handle_dnrs
= dict_new();
7366 dict_set_free_data(handle_dnrs
, free
);
7367 plain_dnrs
= dict_new();
7368 dict_set_free_data(plain_dnrs
, free
);
7369 mask_dnrs
= dict_new();
7370 dict_set_free_data(mask_dnrs
, free
);
7372 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7373 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7374 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7375 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7376 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7377 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7378 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7379 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7380 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7381 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7383 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7384 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7386 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7387 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7388 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7389 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7390 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7392 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7393 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7394 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7395 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7396 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7397 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7399 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7400 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7401 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7402 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7404 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7405 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7406 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7407 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7408 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7409 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7410 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7411 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7412 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7413 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7415 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7416 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7417 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7418 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7419 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7420 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7421 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7422 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7423 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7424 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7425 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7426 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7427 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7428 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7430 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7431 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7432 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7433 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7434 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7436 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7437 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7439 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7440 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7441 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7442 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7443 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7444 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7445 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7446 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7447 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7448 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7449 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7450 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7452 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7453 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7455 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7456 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7457 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7458 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7460 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7461 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7462 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7463 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7464 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7466 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7467 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7468 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7469 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7470 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7471 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7472 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7474 /* Channel options */
7475 DEFINE_CHANNEL_OPTION(defaulttopic
);
7476 DEFINE_CHANNEL_OPTION(topicmask
);
7477 DEFINE_CHANNEL_OPTION(greeting
);
7478 DEFINE_CHANNEL_OPTION(usergreeting
);
7479 DEFINE_CHANNEL_OPTION(modes
);
7480 DEFINE_CHANNEL_OPTION(enfops
);
7481 DEFINE_CHANNEL_OPTION(enfhalfops
);
7482 DEFINE_CHANNEL_OPTION(giveops
);
7483 DEFINE_CHANNEL_OPTION(givehalfops
);
7484 DEFINE_CHANNEL_OPTION(protect
);
7485 DEFINE_CHANNEL_OPTION(enfmodes
);
7486 DEFINE_CHANNEL_OPTION(enftopic
);
7487 DEFINE_CHANNEL_OPTION(pubcmd
);
7488 DEFINE_CHANNEL_OPTION(givevoice
);
7489 DEFINE_CHANNEL_OPTION(userinfo
);
7490 DEFINE_CHANNEL_OPTION(dynlimit
);
7491 DEFINE_CHANNEL_OPTION(topicsnarf
);
7492 DEFINE_CHANNEL_OPTION(nodelete
);
7493 DEFINE_CHANNEL_OPTION(toys
);
7494 DEFINE_CHANNEL_OPTION(setters
);
7495 DEFINE_CHANNEL_OPTION(topicrefresh
);
7496 DEFINE_CHANNEL_OPTION(ctcpusers
);
7497 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7498 DEFINE_CHANNEL_OPTION(inviteme
);
7500 DEFINE_CHANNEL_OPTION(offchannel
);
7501 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7503 /* Alias set topic to set defaulttopic for compatibility. */
7504 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7507 DEFINE_USER_OPTION(noautoop
);
7508 DEFINE_USER_OPTION(autoinvite
);
7509 DEFINE_USER_OPTION(info
);
7511 /* Alias uset autovoice to uset autoop. */
7512 modcmd_register(chanserv_module
, "uset noautovoice", user_opt_noautoop
, 1, 0, NULL
);
7514 note_types
= dict_new();
7515 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7518 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7519 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7520 service_register(chanserv
)->trigger
= '!';
7521 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7523 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7525 if(chanserv_conf
.channel_expire_frequency
)
7526 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7528 if(chanserv_conf
.refresh_period
)
7530 time_t next_refresh
;
7531 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7532 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7535 reg_exit_func(chanserv_db_cleanup
);
7536 message_register_table(msgtab
);