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." },
246 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
247 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
248 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
250 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
251 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
252 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
253 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
254 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
255 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
256 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
257 { "CSMSG_SET_MODES", "$bModes $b %s" },
258 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
259 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
260 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
261 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
263 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
264 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
266 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
267 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
268 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
269 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
271 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
273 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
274 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
275 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
276 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
277 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
278 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
279 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
280 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
281 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
282 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
283 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
284 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
285 { "CSMSG_USET_INFO", "$bInfo $b %s" },
287 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
288 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
289 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
290 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
291 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
292 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
293 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
294 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
295 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
296 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
297 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
298 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
299 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
300 { "CSMSG_PROTECT_NONE", "No users will be protected." },
301 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
302 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
303 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
304 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
305 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
306 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
307 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
308 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
309 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
310 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
311 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
312 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
314 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
315 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
316 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
317 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
318 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
319 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
320 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
321 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
323 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
324 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
325 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
327 /* Channel userlist */
328 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
329 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
330 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
331 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
332 { "CSMSG_BANS_HEADER", "$bBans in %s$b" },
334 /* Channel note list */
335 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
336 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
337 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
338 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
339 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
340 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
341 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
342 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
343 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
344 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
345 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
346 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
347 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
348 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
349 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
351 /* Channel [un]suspension */
352 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
353 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
354 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
355 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
356 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
357 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
358 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
360 /* Access information */
361 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
362 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
363 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
364 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
365 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
366 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
367 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
368 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
369 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
370 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
371 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
373 /* Seen information */
374 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
375 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
376 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
377 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
379 /* Names information */
380 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
381 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
383 /* Channel information */
384 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
385 { "CSMSG_BAR", "----------------------------------------"},
386 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
387 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
388 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
389 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
390 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
391 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
392 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
393 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
394 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
395 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
396 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
400 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
401 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
402 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
403 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
404 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
405 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
406 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
408 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
409 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
410 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
411 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
412 { "CSMSG_PEEK_OPS", "$bOps:$b" },
413 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
414 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
416 /* Network information */
417 { "CSMSG_NETWORK_INFO", "Network Information:" },
418 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
419 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
420 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
421 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
422 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
423 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
424 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
425 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
428 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
429 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
430 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
432 /* Channel searches */
433 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
434 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
435 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
436 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
438 /* Channel configuration */
439 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
440 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
441 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
442 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
445 { "CSMSG_USER_OPTIONS", "User Options:" },
446 { "CSMSG_USER_PROTECTED", "That user is protected." },
449 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
450 { "CSMSG_PING_RESPONSE", "Pong!" },
451 { "CSMSG_WUT_RESPONSE", "wut" },
452 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
453 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
454 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
455 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
456 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
457 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
458 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
461 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
465 /* eject_user and unban_user flags */
466 #define ACTION_KICK 0x0001
467 #define ACTION_BAN 0x0002
468 #define ACTION_ADD_BAN 0x0004
469 #define ACTION_ADD_TIMED_BAN 0x0008
470 #define ACTION_UNBAN 0x0010
471 #define ACTION_DEL_BAN 0x0020
473 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
474 #define MODELEN 40 + KEYLEN
478 #define CSFUNC_ARGS user, channel, argc, argv, cmd
480 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
481 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
482 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
483 reply("MSG_MISSING_PARAMS", argv[0]); \
487 DECLARE_LIST(dnrList
, struct do_not_register
*);
488 DEFINE_LIST(dnrList
, struct do_not_register
*);
490 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
492 struct userNode
*chanserv
;
495 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
496 static struct log_type
*CS_LOG
;
500 struct channelList support_channels
;
501 struct mod_chanmode default_modes
;
503 unsigned long db_backup_frequency
;
504 unsigned long channel_expire_frequency
;
507 unsigned int adjust_delay
;
508 long channel_expire_delay
;
509 unsigned int nodelete_level
;
511 unsigned int adjust_threshold
;
512 int join_flood_threshold
;
514 unsigned int greeting_length
;
515 unsigned int refresh_period
;
516 unsigned int giveownership_period
;
518 unsigned int max_owned
;
519 unsigned int max_chan_users
;
520 unsigned int max_chan_bans
;
521 unsigned int max_userinfo_length
;
523 struct string_list
*set_shows
;
524 struct string_list
*eightball
;
525 struct string_list
*old_ban_names
;
527 const char *ctcp_short_ban_duration
;
528 const char *ctcp_long_ban_duration
;
530 const char *irc_operator_epithet
;
531 const char *network_helper_epithet
;
532 const char *support_helper_epithet
;
537 struct userNode
*user
;
538 struct userNode
*bot
;
539 struct chanNode
*channel
;
541 unsigned short lowest
;
542 unsigned short highest
;
543 struct userData
**users
;
544 struct helpfile_table table
;
547 enum note_access_type
549 NOTE_SET_CHANNEL_ACCESS
,
550 NOTE_SET_CHANNEL_SETTER
,
554 enum note_visible_type
557 NOTE_VIS_CHANNEL_USERS
,
563 enum note_access_type set_access_type
;
565 unsigned int min_opserv
;
566 unsigned short min_ulevel
;
568 enum note_visible_type visible_type
;
569 unsigned int max_length
;
576 struct note_type
*type
;
577 char setter
[NICKSERV_HANDLE_LEN
+1];
581 static unsigned int registered_channels
;
582 static unsigned int banCount
;
584 static const struct {
587 unsigned short level
;
589 } accessLevels
[] = { /* MUST be orderd less to most! */
590 { "peon", "Peon", UL_PEON
, '+' },
591 { "halfop", "HalfOp", UL_HALFOP
, '%' },
592 { "op", "Op", UL_OP
, '@' },
593 { "manager", "Manager", UL_MANAGER
, '%' },
594 { "coowner", "Coowner", UL_COOWNER
, '*' },
595 { "owner", "Owner", UL_OWNER
, '!' },
596 { "helper", "BUG:", UL_HELPER
, 'X' }
599 static const struct {
602 unsigned short default_value
;
603 unsigned int old_idx
;
604 unsigned int old_flag
;
605 unsigned short flag_value
;
607 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
608 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
609 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
610 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
611 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
612 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
613 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
614 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
615 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
616 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
617 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
618 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
619 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
622 struct charOptionValues
{
625 } protectValues
[] = {
626 { 'a', "CSMSG_PROTECT_ALL" },
627 { 'e', "CSMSG_PROTECT_EQUAL" },
628 { 'l', "CSMSG_PROTECT_LOWER" },
629 { 'n', "CSMSG_PROTECT_NONE" }
631 { 'd', "CSMSG_TOYS_DISABLED" },
632 { 'n', "CSMSG_TOYS_PRIVATE" },
633 { 'p', "CSMSG_TOYS_PUBLIC" }
634 }, topicRefreshValues
[] = {
635 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
636 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
637 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
638 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
639 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
640 }, ctcpReactionValues
[] = {
641 { 'k', "CSMSG_CTCPREACTION_KICK" },
642 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
643 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
644 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
647 static const struct {
651 unsigned int old_idx
;
653 struct charOptionValues
*values
;
655 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
656 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
657 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
658 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
661 struct userData
*helperList
;
662 struct chanData
*channelList
;
663 static struct module *chanserv_module
;
664 static unsigned int userCount
;
666 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
667 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
668 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
671 user_level_from_name(const char *name
, unsigned short clamp_level
)
673 unsigned int level
= 0, ii
;
675 level
= strtoul(name
, NULL
, 10);
676 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
677 if(!irccasecmp(name
, accessLevels
[ii
].name
))
678 level
= accessLevels
[ii
].level
;
679 if(level
> clamp_level
)
685 user_level_name_from_level(int level
)
693 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
694 if(level
>= accessLevels
[ii
].level
)
695 highest
= accessLevels
[ii
].title
;
701 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
704 *minl
= strtoul(arg
, &sep
, 10);
712 *maxl
= strtoul(sep
+1, &sep
, 10);
720 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
722 struct userData
*uData
, **head
;
724 if(!channel
|| !handle
)
727 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
728 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
730 for(uData
= helperList
;
731 uData
&& uData
->handle
!= handle
;
732 uData
= uData
->next
);
736 uData
= calloc(1, sizeof(struct userData
));
737 uData
->handle
= handle
;
739 uData
->access
= UL_HELPER
;
745 uData
->next
= helperList
;
747 helperList
->prev
= uData
;
755 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
756 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
759 head
= &(channel
->users
);
762 if(uData
&& (uData
!= *head
))
764 /* Shuffle the user to the head of whatever list he was in. */
766 uData
->next
->prev
= uData
->prev
;
768 uData
->prev
->next
= uData
->next
;
774 (**head
).prev
= uData
;
781 /* Returns non-zero if user has at least the minimum access.
782 * exempt_owner is set when handling !set, so the owner can set things
785 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
787 struct userData
*uData
;
788 struct chanData
*cData
= channel
->channel_info
;
789 unsigned short minimum
= cData
->lvlOpts
[opt
];
792 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
795 if(minimum
<= uData
->access
)
797 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
802 /* Scan for other users authenticated to the same handle
803 still in the channel. If so, keep them listed as present.
805 user is optional, if not null, it skips checking that userNode
806 (for the handle_part function) */
808 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
812 if(IsSuspended(uData
->channel
)
813 || IsUserSuspended(uData
)
814 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
826 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
828 unsigned int eflags
, argc
;
830 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
832 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
833 if(!channel
->channel_info
834 || IsSuspended(channel
->channel_info
)
836 || !ircncasecmp(text
, "ACTION ", 7))
838 /* Figure out the minimum level needed to CTCP the channel */
839 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
841 /* We need to enforce against them; do so. */
844 argv
[1] = user
->nick
;
846 if(GetUserMode(channel
, user
))
847 eflags
|= ACTION_KICK
;
848 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
849 default: case 'k': /* just do the kick */ break;
851 eflags
|= ACTION_BAN
;
854 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
855 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
858 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
859 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
862 argv
[argc
++] = bad_ctcp_reason
;
863 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
867 chanserv_create_note_type(const char *name
)
869 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
870 strcpy(ntype
->name
, name
);
872 dict_insert(note_types
, ntype
->name
, ntype
);
877 chanserv_deref_note_type(void *data
)
879 struct note_type
*ntype
= data
;
881 if(--ntype
->refs
> 0)
887 chanserv_flush_note_type(struct note_type
*ntype
)
889 struct chanData
*cData
;
890 for(cData
= channelList
; cData
; cData
= cData
->next
)
891 dict_remove(cData
->notes
, ntype
->name
);
895 chanserv_truncate_notes(struct note_type
*ntype
)
897 struct chanData
*cData
;
899 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
901 for(cData
= channelList
; cData
; cData
= cData
->next
) {
902 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
905 if(strlen(note
->note
) <= ntype
->max_length
)
907 dict_remove2(cData
->notes
, ntype
->name
, 1);
908 note
= realloc(note
, size
);
909 note
->note
[ntype
->max_length
] = 0;
910 dict_insert(cData
->notes
, ntype
->name
, note
);
914 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
917 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
920 unsigned int len
= strlen(text
);
922 if(len
> type
->max_length
) len
= type
->max_length
;
923 note
= calloc(1, sizeof(*note
) + len
);
925 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
926 memcpy(note
->note
, text
, len
);
928 dict_insert(channel
->notes
, type
->name
, note
);
934 chanserv_free_note(void *data
)
936 struct note
*note
= data
;
938 chanserv_deref_note_type(note
->type
);
939 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
943 static MODCMD_FUNC(cmd_createnote
) {
944 struct note_type
*ntype
;
945 unsigned int arg
= 1, existed
= 0, max_length
;
947 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
950 ntype
= chanserv_create_note_type(argv
[arg
]);
951 if(!irccasecmp(argv
[++arg
], "privileged"))
954 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
955 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
957 else if(!irccasecmp(argv
[arg
], "channel"))
959 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
962 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
965 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
966 ntype
->set_access
.min_ulevel
= ulvl
;
968 else if(!irccasecmp(argv
[arg
], "setter"))
970 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
974 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
978 if(!irccasecmp(argv
[++arg
], "privileged"))
979 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
980 else if(!irccasecmp(argv
[arg
], "channel_users"))
981 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
982 else if(!irccasecmp(argv
[arg
], "all"))
983 ntype
->visible_type
= NOTE_VIS_ALL
;
985 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
989 if((arg
+1) >= argc
) {
990 reply("MSG_MISSING_PARAMS", argv
[0]);
993 max_length
= strtoul(argv
[++arg
], NULL
, 0);
994 if(max_length
< 20 || max_length
> 450)
996 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
999 if(existed
&& (max_length
< ntype
->max_length
))
1001 ntype
->max_length
= max_length
;
1002 chanserv_truncate_notes(ntype
);
1004 ntype
->max_length
= max_length
;
1007 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1009 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1014 dict_remove(note_types
, ntype
->name
);
1018 static MODCMD_FUNC(cmd_removenote
) {
1019 struct note_type
*ntype
;
1022 ntype
= dict_find(note_types
, argv
[1], NULL
);
1023 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1026 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1033 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1036 chanserv_flush_note_type(ntype
);
1038 dict_remove(note_types
, argv
[1]);
1039 reply("CSMSG_NOTE_DELETED", argv
[1]);
1044 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1048 if(orig
->modes_set
& change
->modes_clear
)
1050 if(orig
->modes_clear
& change
->modes_set
)
1052 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1053 && strcmp(orig
->new_key
, change
->new_key
))
1055 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1056 && (orig
->new_limit
!= change
->new_limit
))
1061 static char max_length_text
[MAXLEN
+1][16];
1063 static struct helpfile_expansion
1064 chanserv_expand_variable(const char *variable
)
1066 struct helpfile_expansion exp
;
1068 if(!irccasecmp(variable
, "notes"))
1071 exp
.type
= HF_TABLE
;
1072 exp
.value
.table
.length
= 1;
1073 exp
.value
.table
.width
= 3;
1074 exp
.value
.table
.flags
= 0;
1075 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1076 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1077 exp
.value
.table
.contents
[0][0] = "Note Type";
1078 exp
.value
.table
.contents
[0][1] = "Visibility";
1079 exp
.value
.table
.contents
[0][2] = "Max Length";
1080 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1082 struct note_type
*ntype
= iter_data(it
);
1085 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1086 row
= exp
.value
.table
.length
++;
1087 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1088 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1089 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1090 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1092 if(!max_length_text
[ntype
->max_length
][0])
1093 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1094 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1099 exp
.type
= HF_STRING
;
1100 exp
.value
.str
= NULL
;
1104 static struct chanData
*
1105 register_channel(struct chanNode
*cNode
, char *registrar
)
1107 struct chanData
*channel
;
1108 enum levelOption lvlOpt
;
1109 enum charOption chOpt
;
1111 channel
= calloc(1, sizeof(struct chanData
));
1113 channel
->notes
= dict_new();
1114 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1116 channel
->registrar
= strdup(registrar
);
1117 channel
->registered
= now
;
1118 channel
->visited
= now
;
1119 channel
->limitAdjusted
= now
;
1120 channel
->ownerTransfer
= now
;
1121 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1122 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1123 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1124 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1125 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1127 channel
->prev
= NULL
;
1128 channel
->next
= channelList
;
1131 channelList
->prev
= channel
;
1132 channelList
= channel
;
1133 registered_channels
++;
1135 channel
->channel
= cNode
;
1137 cNode
->channel_info
= channel
;
1142 static struct userData
*
1143 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1145 struct userData
*ud
;
1147 if(access
> UL_OWNER
)
1150 ud
= calloc(1, sizeof(*ud
));
1151 ud
->channel
= channel
;
1152 ud
->handle
= handle
;
1154 ud
->access
= access
;
1155 ud
->info
= info
? strdup(info
) : NULL
;
1158 ud
->next
= channel
->users
;
1160 channel
->users
->prev
= ud
;
1161 channel
->users
= ud
;
1163 channel
->userCount
++;
1167 ud
->u_next
= ud
->handle
->channels
;
1169 ud
->u_next
->u_prev
= ud
;
1170 ud
->handle
->channels
= ud
;
1175 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1178 del_channel_user(struct userData
*user
, int do_gc
)
1180 struct chanData
*channel
= user
->channel
;
1182 channel
->userCount
--;
1186 user
->prev
->next
= user
->next
;
1188 channel
->users
= user
->next
;
1190 user
->next
->prev
= user
->prev
;
1193 user
->u_prev
->u_next
= user
->u_next
;
1195 user
->handle
->channels
= user
->u_next
;
1197 user
->u_next
->u_prev
= user
->u_prev
;
1201 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1202 unregister_channel(channel
, "lost all users.");
1205 static void expire_ban(void *data
);
1207 static struct banData
*
1208 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1211 unsigned int ii
, l1
, l2
;
1216 bd
= malloc(sizeof(struct banData
));
1218 bd
->channel
= channel
;
1220 bd
->triggered
= triggered
;
1221 bd
->expires
= expires
;
1223 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1225 extern const char *hidden_host_suffix
;
1226 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1230 l2
= strlen(old_name
);
1233 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1235 new_mask
= alloca(MAXLEN
);
1236 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1239 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1241 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1242 bd
->reason
= strdup(reason
);
1245 timeq_add(expires
, expire_ban
, bd
);
1248 bd
->next
= channel
->bans
;
1250 channel
->bans
->prev
= bd
;
1252 channel
->banCount
++;
1259 del_channel_ban(struct banData
*ban
)
1261 ban
->channel
->banCount
--;
1265 ban
->prev
->next
= ban
->next
;
1267 ban
->channel
->bans
= ban
->next
;
1270 ban
->next
->prev
= ban
->prev
;
1273 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1282 expire_ban(void *data
)
1284 struct banData
*bd
= data
;
1285 if(!IsSuspended(bd
->channel
))
1287 struct banList bans
;
1288 struct mod_chanmode change
;
1290 bans
= bd
->channel
->channel
->banlist
;
1291 mod_chanmode_init(&change
);
1292 for(ii
=0; ii
<bans
.used
; ii
++)
1294 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1297 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1298 change
.args
[0].u
.hostmask
= bd
->mask
;
1299 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1305 del_channel_ban(bd
);
1308 static void chanserv_expire_suspension(void *data
);
1311 unregister_channel(struct chanData
*channel
, const char *reason
)
1313 struct mod_chanmode change
;
1314 char msgbuf
[MAXLEN
];
1316 /* After channel unregistration, the following must be cleaned
1318 - Channel information.
1321 - Channel suspension data.
1322 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1328 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1332 mod_chanmode_init(&change
);
1333 change
.modes_clear
|= MODE_REGISTERED
;
1334 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1337 while(channel
->users
)
1338 del_channel_user(channel
->users
, 0);
1340 while(channel
->bans
)
1341 del_channel_ban(channel
->bans
);
1343 free(channel
->topic
);
1344 free(channel
->registrar
);
1345 free(channel
->greeting
);
1346 free(channel
->user_greeting
);
1347 free(channel
->topic_mask
);
1350 channel
->prev
->next
= channel
->next
;
1352 channelList
= channel
->next
;
1355 channel
->next
->prev
= channel
->prev
;
1357 if(channel
->suspended
)
1359 struct chanNode
*cNode
= channel
->channel
;
1360 struct suspended
*suspended
, *next_suspended
;
1362 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1364 next_suspended
= suspended
->previous
;
1365 free(suspended
->suspender
);
1366 free(suspended
->reason
);
1367 if(suspended
->expires
)
1368 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1373 cNode
->channel_info
= NULL
;
1375 channel
->channel
->channel_info
= NULL
;
1377 dict_delete(channel
->notes
);
1378 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1379 if(!IsSuspended(channel
))
1380 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1381 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1382 UnlockChannel(channel
->channel
);
1384 registered_channels
--;
1388 expire_channels(UNUSED_ARG(void *data
))
1390 struct chanData
*channel
, *next
;
1391 struct userData
*user
;
1392 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1394 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1395 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1397 for(channel
= channelList
; channel
; channel
= next
)
1399 next
= channel
->next
;
1401 /* See if the channel can be expired. */
1402 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1403 || IsProtected(channel
))
1406 /* Make sure there are no high-ranking users still in the channel. */
1407 for(user
=channel
->users
; user
; user
=user
->next
)
1408 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1413 /* Unregister the channel */
1414 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1415 unregister_channel(channel
, "registration expired.");
1418 if(chanserv_conf
.channel_expire_frequency
)
1419 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1423 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1425 char protect
= channel
->chOpts
[chProtect
];
1426 struct userData
*cs_victim
, *cs_aggressor
;
1428 /* Don't protect if no one is to be protected, someone is attacking
1429 himself, or if the aggressor is an IRC Operator. */
1430 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1433 /* Don't protect if the victim isn't authenticated (because they
1434 can't be a channel user), unless we are to protect non-users
1436 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1437 if(protect
!= 'a' && !cs_victim
)
1440 /* Protect if the aggressor isn't a user because at this point,
1441 the aggressor can only be less than or equal to the victim. */
1442 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1446 /* If the aggressor was a user, then the victim can't be helped. */
1453 if(cs_victim
->access
> cs_aggressor
->access
)
1458 if(cs_victim
->access
>= cs_aggressor
->access
)
1467 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1469 struct chanData
*cData
= channel
->channel_info
;
1470 struct userData
*cs_victim
;
1472 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1473 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1474 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1476 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1484 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1486 struct chanData
*cData
= channel
->channel_info
;
1487 struct userData
*cs_victim
;
1489 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1490 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1491 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1493 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1502 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1504 if(IsService(victim
))
1506 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1510 if(protect_user(victim
, user
, channel
->channel_info
))
1512 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1520 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1522 if(IsService(victim
))
1524 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1528 if(protect_user(victim
, user
, channel
->channel_info
))
1530 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1537 static struct do_not_register
*
1538 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1540 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1541 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1542 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1543 strcpy(dnr
->reason
, reason
);
1545 if(dnr
->chan_name
[0] == '*')
1546 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1547 else if(strpbrk(dnr
->chan_name
, "*?"))
1548 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1550 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1554 static struct dnrList
1555 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1557 struct dnrList list
;
1559 struct do_not_register
*dnr
;
1561 dnrList_init(&list
);
1562 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1563 dnrList_append(&list
, dnr
);
1564 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1565 dnrList_append(&list
, dnr
);
1567 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1568 if(match_ircglob(chan_name
, iter_key(it
)))
1569 dnrList_append(&list
, iter_data(it
));
1574 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1576 struct dnrList list
;
1577 struct do_not_register
*dnr
;
1579 char buf
[INTERVALLEN
];
1581 list
= chanserv_find_dnrs(chan_name
, handle
);
1582 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1584 dnr
= list
.list
[ii
];
1587 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1588 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1591 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1594 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1599 struct do_not_register
*
1600 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1602 struct do_not_register
*dnr
;
1605 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1609 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1611 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1612 if(match_ircglob(chan_name
, iter_key(it
)))
1613 return iter_data(it
);
1618 static CHANSERV_FUNC(cmd_noregister
)
1621 struct do_not_register
*dnr
;
1622 char buf
[INTERVALLEN
];
1623 unsigned int matches
;
1629 reply("CSMSG_DNR_SEARCH_RESULTS");
1632 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1634 dnr
= iter_data(it
);
1636 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1638 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1641 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1643 dnr
= iter_data(it
);
1645 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1647 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1650 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1652 dnr
= iter_data(it
);
1654 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1656 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1661 reply("MSG_MATCH_COUNT", matches
);
1663 reply("MSG_NO_MATCHES");
1669 if(!IsChannelName(target
) && (*target
!= '*'))
1671 reply("CSMSG_NOT_DNR", target
);
1677 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1678 if((*target
== '*') && !get_handle_info(target
+ 1))
1680 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1683 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1684 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1688 reply("CSMSG_DNR_SEARCH_RESULTS");
1691 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1693 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1695 reply("MSG_NO_MATCHES");
1699 static CHANSERV_FUNC(cmd_allowregister
)
1701 const char *chan_name
= argv
[1];
1703 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1705 dict_remove(handle_dnrs
, chan_name
+1);
1706 reply("CSMSG_DNR_REMOVED", chan_name
);
1708 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1710 dict_remove(plain_dnrs
, chan_name
);
1711 reply("CSMSG_DNR_REMOVED", chan_name
);
1713 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1715 dict_remove(mask_dnrs
, chan_name
);
1716 reply("CSMSG_DNR_REMOVED", chan_name
);
1720 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1727 chanserv_get_owned_count(struct handle_info
*hi
)
1729 struct userData
*cList
;
1732 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1733 if(cList
->access
== UL_OWNER
)
1738 static CHANSERV_FUNC(cmd_register
)
1740 struct handle_info
*handle
;
1741 struct chanData
*cData
;
1742 struct modeNode
*mn
;
1743 char reason
[MAXLEN
];
1745 unsigned int new_channel
, force
=0;
1746 struct do_not_register
*dnr
;
1750 if(channel
->channel_info
)
1752 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1756 if(channel
->bad_channel
)
1758 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1763 && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1765 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1770 chan_name
= channel
->name
;
1776 reply("MSG_MISSING_PARAMS", cmd
->name
);
1777 svccmd_send_help_brief(user
, chanserv
, cmd
);
1780 if(!IsChannelName(argv
[1]))
1782 reply("MSG_NOT_CHANNEL_NAME");
1786 if(opserv_bad_channel(argv
[1]))
1788 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1793 chan_name
= argv
[1];
1796 if(argc
>= (new_channel
+2))
1798 if(!IsHelping(user
))
1800 reply("CSMSG_PROXY_FORBIDDEN");
1804 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1806 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1807 dnr
= chanserv_is_dnr(chan_name
, handle
);
1811 handle
= user
->handle_info
;
1812 dnr
= chanserv_is_dnr(chan_name
, handle
);
1816 if(!IsHelping(user
))
1817 reply("CSMSG_DNR_CHANNEL", chan_name
);
1819 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1823 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1825 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1830 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1832 cData
= register_channel(channel
, user
->handle_info
->handle
);
1833 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1834 cData
->modes
= chanserv_conf
.default_modes
;
1836 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1837 if (IsOffChannel(cData
))
1839 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
1843 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
1844 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1845 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1847 mod_chanmode_announce(chanserv
, channel
, change
);
1848 mod_chanmode_free(change
);
1851 /* Initialize the channel's max user record. */
1852 cData
->max
= channel
->members
.used
;
1854 if(handle
!= user
->handle_info
)
1855 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1857 reply("CSMSG_REG_SUCCESS", channel
->name
);
1859 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1860 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1865 make_confirmation_string(struct userData
*uData
)
1867 static char strbuf
[16];
1872 for(src
= uData
->handle
->handle
; *src
; )
1873 accum
= accum
* 31 + toupper(*src
++);
1875 for(src
= uData
->channel
->channel
->name
; *src
; )
1876 accum
= accum
* 31 + toupper(*src
++);
1877 sprintf(strbuf
, "%08x", accum
);
1881 static CHANSERV_FUNC(cmd_unregister
)
1884 char reason
[MAXLEN
];
1885 struct chanData
*cData
;
1886 struct userData
*uData
;
1888 cData
= channel
->channel_info
;
1891 reply("CSMSG_NOT_REGISTERED", channel
->name
);
1895 uData
= GetChannelUser(cData
, user
->handle_info
);
1896 if(!uData
|| (uData
->access
< UL_OWNER
))
1898 reply("CSMSG_NO_ACCESS");
1902 if(IsProtected(cData
))
1904 reply("CSMSG_UNREG_NODELETE", channel
->name
);
1908 if(!IsHelping(user
))
1910 const char *confirm_string
;
1911 if(IsSuspended(cData
))
1913 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
1916 confirm_string
= make_confirmation_string(uData
);
1917 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
1919 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
1924 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
1925 name
= strdup(channel
->name
);
1926 unregister_channel(cData
, reason
);
1927 reply("CSMSG_UNREG_SUCCESS", name
);
1932 static CHANSERV_FUNC(cmd_move
)
1934 struct mod_chanmode change
;
1935 struct chanNode
*target
;
1936 struct modeNode
*mn
;
1937 struct userData
*uData
;
1938 char reason
[MAXLEN
];
1939 struct do_not_register
*dnr
;
1943 if(IsProtected(channel
->channel_info
))
1945 reply("CSMSG_MOVE_NODELETE", channel
->name
);
1949 if(!IsChannelName(argv
[1]))
1951 reply("MSG_NOT_CHANNEL_NAME");
1955 if(opserv_bad_channel(argv
[1]))
1957 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1961 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
1963 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
1965 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
1967 if(!IsHelping(user
))
1968 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
1970 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
1976 mod_chanmode_init(&change
);
1977 if(!(target
= GetChannel(argv
[1])))
1979 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1980 if(!IsSuspended(channel
->channel_info
))
1981 AddChannelUser(chanserv
, target
);
1983 else if(target
->channel_info
)
1985 reply("CSMSG_ALREADY_REGGED", target
->name
);
1988 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
1989 && !IsHelping(user
))
1991 reply("CSMSG_MUST_BE_OPPED", target
->name
);
1994 else if(!IsSuspended(channel
->channel_info
))
1997 change
.args
[0].mode
= MODE_CHANOP
;
1998 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
1999 mod_chanmode_announce(chanserv
, target
, &change
);
2004 /* Clear MODE_REGISTERED from old channel, add it to new. */
2006 change
.modes_clear
= MODE_REGISTERED
;
2007 mod_chanmode_announce(chanserv
, channel
, &change
);
2008 change
.modes_clear
= 0;
2009 change
.modes_set
= MODE_REGISTERED
;
2010 mod_chanmode_announce(chanserv
, target
, &change
);
2013 /* Move the channel_info to the target channel; it
2014 shouldn't be necessary to clear timeq callbacks
2015 for the old channel. */
2016 target
->channel_info
= channel
->channel_info
;
2017 target
->channel_info
->channel
= target
;
2018 channel
->channel_info
= NULL
;
2020 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2022 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2023 if(!IsSuspended(target
->channel_info
))
2025 char reason2
[MAXLEN
];
2026 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2027 DelChannelUser(chanserv
, channel
, reason2
, 0);
2029 UnlockChannel(channel
);
2030 LockChannel(target
);
2031 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2036 merge_users(struct chanData
*source
, struct chanData
*target
)
2038 struct userData
*suData
, *tuData
, *next
;
2044 /* Insert the source's users into the scratch area. */
2045 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2046 dict_insert(merge
, suData
->handle
->handle
, suData
);
2048 /* Iterate through the target's users, looking for
2049 users common to both channels. The lower access is
2050 removed from either the scratch area or target user
2052 for(tuData
= target
->users
; tuData
; tuData
= next
)
2054 struct userData
*choice
;
2056 next
= tuData
->next
;
2058 /* If a source user exists with the same handle as a target
2059 channel's user, resolve the conflict by removing one. */
2060 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2064 /* Pick the data we want to keep. */
2065 /* If the access is the same, use the later seen time. */
2066 if(suData
->access
== tuData
->access
)
2067 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2068 else /* Otherwise, keep the higher access level. */
2069 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2071 /* Remove the user that wasn't picked. */
2072 if(choice
== tuData
)
2074 dict_remove(merge
, suData
->handle
->handle
);
2075 del_channel_user(suData
, 0);
2078 del_channel_user(tuData
, 0);
2081 /* Move the remaining users to the target channel. */
2082 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2084 suData
= iter_data(it
);
2086 /* Insert the user into the target channel's linked list. */
2087 suData
->prev
= NULL
;
2088 suData
->next
= target
->users
;
2089 suData
->channel
= target
;
2092 target
->users
->prev
= suData
;
2093 target
->users
= suData
;
2095 /* Update the user counts for the target channel; the
2096 source counts are left alone. */
2097 target
->userCount
++;
2100 /* Possible to assert (source->users == NULL) here. */
2101 source
->users
= NULL
;
2106 merge_bans(struct chanData
*source
, struct chanData
*target
)
2108 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2110 /* Hold on to the original head of the target ban list
2111 to avoid comparing source bans with source bans. */
2112 tFront
= target
->bans
;
2114 /* Perform a totally expensive O(n*m) merge, ick. */
2115 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2117 /* Flag to track whether the ban's been moved
2118 to the destination yet. */
2121 /* Possible to assert (sbData->prev == NULL) here. */
2122 sNext
= sbData
->next
;
2124 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2126 tNext
= tbData
->next
;
2128 /* Perform two comparisons between each source
2129 and target ban, conflicts are resolved by
2130 keeping the broader ban and copying the later
2131 expiration and triggered time. */
2132 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2134 /* There is a broader ban in the target channel that
2135 overrides one in the source channel; remove the
2136 source ban and break. */
2137 if(sbData
->expires
> tbData
->expires
)
2138 tbData
->expires
= sbData
->expires
;
2139 if(sbData
->triggered
> tbData
->triggered
)
2140 tbData
->triggered
= sbData
->triggered
;
2141 del_channel_ban(sbData
);
2144 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2146 /* There is a broader ban in the source channel that
2147 overrides one in the target channel; remove the
2148 target ban, fall through and move the source over. */
2149 if(tbData
->expires
> sbData
->expires
)
2150 sbData
->expires
= tbData
->expires
;
2151 if(tbData
->triggered
> sbData
->triggered
)
2152 sbData
->triggered
= tbData
->triggered
;
2153 if(tbData
== tFront
)
2155 del_channel_ban(tbData
);
2158 /* Source bans can override multiple target bans, so
2159 we allow a source to run through this loop multiple
2160 times, but we can only move it once. */
2165 /* Remove the source ban from the source ban list. */
2167 sbData
->next
->prev
= sbData
->prev
;
2169 /* Modify the source ban's associated channel. */
2170 sbData
->channel
= target
;
2172 /* Insert the ban into the target channel's linked list. */
2173 sbData
->prev
= NULL
;
2174 sbData
->next
= target
->bans
;
2177 target
->bans
->prev
= sbData
;
2178 target
->bans
= sbData
;
2180 /* Update the user counts for the target channel. */
2185 /* Possible to assert (source->bans == NULL) here. */
2186 source
->bans
= NULL
;
2190 merge_data(struct chanData
*source
, struct chanData
*target
)
2192 if(source
->visited
> target
->visited
)
2193 target
->visited
= source
->visited
;
2197 merge_channel(struct chanData
*source
, struct chanData
*target
)
2199 merge_users(source
, target
);
2200 merge_bans(source
, target
);
2201 merge_data(source
, target
);
2204 static CHANSERV_FUNC(cmd_merge
)
2206 struct userData
*target_user
;
2207 struct chanNode
*target
;
2208 char reason
[MAXLEN
];
2212 /* Make sure the target channel exists and is registered to the user
2213 performing the command. */
2214 if(!(target
= GetChannel(argv
[1])))
2216 reply("MSG_INVALID_CHANNEL");
2220 if(!target
->channel_info
)
2222 reply("CSMSG_NOT_REGISTERED", target
->name
);
2226 if(IsProtected(channel
->channel_info
))
2228 reply("CSMSG_MERGE_NODELETE");
2232 if(IsSuspended(target
->channel_info
))
2234 reply("CSMSG_MERGE_SUSPENDED");
2238 if(channel
== target
)
2240 reply("CSMSG_MERGE_SELF");
2244 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2245 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2247 reply("CSMSG_MERGE_NOT_OWNER");
2251 /* Merge the channel structures and associated data. */
2252 merge_channel(channel
->channel_info
, target
->channel_info
);
2253 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2254 unregister_channel(channel
->channel_info
, reason
);
2255 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2259 static CHANSERV_FUNC(cmd_opchan
)
2261 struct mod_chanmode change
;
2262 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2264 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2267 channel
->channel_info
->may_opchan
= 0;
2268 mod_chanmode_init(&change
);
2270 change
.args
[0].mode
= MODE_CHANOP
;
2271 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2272 mod_chanmode_announce(chanserv
, channel
, &change
);
2273 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2277 static CHANSERV_FUNC(cmd_adduser
)
2279 struct userData
*actee
;
2280 struct userData
*actor
;
2281 struct handle_info
*handle
;
2282 unsigned short access
;
2286 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2288 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2292 access
= user_level_from_name(argv
[2], UL_OWNER
);
2295 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2299 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2300 if(actor
->access
<= access
)
2302 reply("CSMSG_NO_BUMP_ACCESS");
2306 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2309 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2311 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2315 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2316 scan_user_presence(actee
, NULL
);
2317 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2321 static CHANSERV_FUNC(cmd_clvl
)
2323 struct handle_info
*handle
;
2324 struct userData
*victim
;
2325 struct userData
*actor
;
2326 unsigned short new_access
;
2327 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2331 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2333 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2336 if(handle
== user
->handle_info
&& !privileged
)
2338 reply("CSMSG_NO_SELF_CLVL");
2342 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2344 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2348 if(actor
->access
<= victim
->access
&& !privileged
)
2350 reply("MSG_USER_OUTRANKED", handle
->handle
);
2354 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2358 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2362 if(new_access
>= actor
->access
&& !privileged
)
2364 reply("CSMSG_NO_BUMP_ACCESS");
2368 victim
->access
= new_access
;
2369 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2373 static CHANSERV_FUNC(cmd_deluser
)
2375 struct handle_info
*handle
;
2376 struct userData
*victim
;
2377 struct userData
*actor
;
2378 unsigned short access
;
2383 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2385 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2388 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2390 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2396 access
= user_level_from_name(argv
[1], UL_OWNER
);
2399 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2402 if(access
!= victim
->access
)
2404 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2410 access
= victim
->access
;
2413 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2415 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2419 chan_name
= strdup(channel
->name
);
2420 del_channel_user(victim
, 1);
2421 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2427 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2429 struct userData
*actor
, *uData
, *next
;
2431 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2433 if(min_access
> max_access
)
2435 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2439 if((actor
->access
<= max_access
) && !IsHelping(user
))
2441 reply("CSMSG_NO_ACCESS");
2445 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2449 if((uData
->access
>= min_access
)
2450 && (uData
->access
<= max_access
)
2451 && match_ircglob(uData
->handle
->handle
, mask
))
2452 del_channel_user(uData
, 1);
2455 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2459 static CHANSERV_FUNC(cmd_mdelowner
)
2461 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2464 static CHANSERV_FUNC(cmd_mdelcoowner
)
2466 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2469 static CHANSERV_FUNC(cmd_mdelmanager
)
2471 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2474 static CHANSERV_FUNC(cmd_mdelop
)
2476 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2479 static CHANSERV_FUNC(cmd_mdelpeon
)
2481 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2484 static CHANSERV_FUNC(cmd_mdelhalfop
)
2486 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2491 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2493 struct banData
*bData
, *next
;
2494 char interval
[INTERVALLEN
];
2499 limit
= now
- duration
;
2500 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2504 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2507 del_channel_ban(bData
);
2511 intervalString(interval
, duration
, user
->handle_info
);
2512 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2517 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2519 struct userData
*actor
, *uData
, *next
;
2520 char interval
[INTERVALLEN
];
2524 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2525 if(min_access
> max_access
)
2527 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2531 if((actor
->access
<= max_access
) && !IsHelping(user
))
2533 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2538 limit
= now
- duration
;
2539 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2543 if((uData
->seen
> limit
) || uData
->present
)
2546 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2547 || (!max_access
&& (uData
->access
< actor
->access
)))
2549 del_channel_user(uData
, 1);
2557 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2559 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2563 static CHANSERV_FUNC(cmd_trim
)
2565 unsigned long duration
;
2566 unsigned short min_level
, max_level
;
2570 duration
= ParseInterval(argv
[2]);
2573 reply("CSMSG_CANNOT_TRIM");
2577 if(!irccasecmp(argv
[1], "bans"))
2579 cmd_trim_bans(user
, channel
, duration
);
2582 else if(!irccasecmp(argv
[1], "users"))
2584 cmd_trim_users(user
, channel
, 0, 0, duration
);
2587 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2589 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2592 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2594 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2599 reply("CSMSG_INVALID_TRIM", argv
[1]);
2604 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2605 to the user. cmd_all takes advantage of this. */
2606 static CHANSERV_FUNC(cmd_up
)
2608 struct mod_chanmode change
;
2609 struct userData
*uData
;
2612 mod_chanmode_init(&change
);
2614 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2615 if(!change
.args
[0].u
.member
)
2618 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2622 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2626 reply("CSMSG_GODMODE_UP", argv
[0]);
2629 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2631 change
.args
[0].mode
= MODE_CHANOP
;
2632 errmsg
= "CSMSG_ALREADY_OPPED";
2634 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2636 change
.args
[0].mode
= MODE_HALFOP
;
2637 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2639 else if(uData
->access
>= UL_PEON
/* channel->channel_info->lvlOpts[lvlGiveVoice]*/)
2641 change
.args
[0].mode
= MODE_VOICE
;
2642 errmsg
= "CSMSG_ALREADY_VOICED";
2647 reply("CSMSG_NO_ACCESS");
2650 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2651 if(!change
.args
[0].mode
)
2654 reply(errmsg
, channel
->name
);
2657 modcmd_chanmode_announce(&change
);
2661 static CHANSERV_FUNC(cmd_down
)
2663 struct mod_chanmode change
;
2665 mod_chanmode_init(&change
);
2667 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2668 if(!change
.args
[0].u
.member
)
2671 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2675 if(!change
.args
[0].u
.member
->modes
)
2678 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2682 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2683 modcmd_chanmode_announce(&change
);
2687 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
)
2689 struct userData
*cList
;
2691 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2693 if(IsSuspended(cList
->channel
)
2694 || IsUserSuspended(cList
)
2695 || !GetUserMode(cList
->channel
->channel
, user
))
2698 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2704 static CHANSERV_FUNC(cmd_upall
)
2706 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2709 static CHANSERV_FUNC(cmd_downall
)
2711 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2714 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2715 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2718 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
)
2720 unsigned int ii
, valid
;
2721 struct userNode
*victim
;
2722 struct mod_chanmode
*change
;
2724 change
= mod_chanmode_alloc(argc
- 1);
2726 for(ii
=valid
=0; ++ii
< argc
; )
2728 if(!(victim
= GetUserH(argv
[ii
])))
2730 change
->args
[valid
].mode
= mode
;
2731 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2732 if(!change
->args
[valid
].u
.member
)
2734 if(validate
&& !validate(user
, channel
, victim
))
2739 change
->argc
= valid
;
2740 if(valid
< (argc
-1))
2741 reply("CSMSG_PROCESS_FAILED");
2744 modcmd_chanmode_announce(change
);
2745 reply(action
, channel
->name
);
2747 mod_chanmode_free(change
);
2751 static CHANSERV_FUNC(cmd_op
)
2753 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2756 static CHANSERV_FUNC(cmd_hop
)
2758 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2761 static CHANSERV_FUNC(cmd_deop
)
2763 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2766 static CHANSERV_FUNC(cmd_dehop
)
2768 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2771 static CHANSERV_FUNC(cmd_voice
)
2773 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2776 static CHANSERV_FUNC(cmd_devoice
)
2778 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2782 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2788 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2790 struct modeNode
*mn
= channel
->members
.list
[ii
];
2792 if(IsService(mn
->user
))
2795 if(!user_matches_glob(mn
->user
, ban
, 1))
2798 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2802 victims
[(*victimCount
)++] = mn
;
2808 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2810 struct userNode
*victim
;
2811 struct modeNode
**victims
;
2812 unsigned int offset
, n
, victimCount
, duration
= 0;
2813 char *reason
= "Bye.", *ban
, *name
;
2814 char interval
[INTERVALLEN
];
2816 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2817 REQUIRE_PARAMS(offset
);
2820 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2821 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2823 /* Truncate the reason to a length of TOPICLEN, as
2824 the ircd does; however, leave room for an ellipsis
2825 and the kicker's nick. */
2826 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2830 if((victim
= GetUserH(argv
[1])))
2832 victims
= alloca(sizeof(victims
[0]));
2833 victims
[0] = GetUserMode(channel
, victim
);
2834 /* XXX: The comparison with ACTION_KICK is just because all
2835 * other actions can work on users outside the channel, and we
2836 * want to allow those (e.g. unbans) in that case. If we add
2837 * some other ejection action for in-channel users, change
2839 victimCount
= victims
[0] ? 1 : 0;
2841 if(IsService(victim
))
2843 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
2847 if((action
== ACTION_KICK
) && !victimCount
)
2849 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
2853 if(protect_user(victim
, user
, channel
->channel_info
))
2855 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2859 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
2860 name
= victim
->nick
;
2864 if(!is_ircmask(argv
[1]))
2866 reply("MSG_NICK_UNKNOWN", argv
[1]);
2870 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
2872 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
2874 reply("CSMSG_MASK_PROTECTED", argv
[1]);
2877 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
2879 victimCount
= 0; /* Dont deop etc ppl who match this */
2881 #ifdef entropy_lameness
2882 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
2884 reply("CSMSG_LAME_MASK", argv
[1]);
2889 if((action
== ACTION_KICK
) && (victimCount
== 0))
2891 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
2895 name
= ban
= strdup(argv
[1]);
2898 /* Truncate the ban in place if necessary; we must ensure
2899 that 'ban' is a valid ban mask before sanitizing it. */
2900 sanitize_ircmask(ban
);
2902 if(action
& ACTION_ADD_BAN
)
2904 struct banData
*bData
, *next
;
2906 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
2908 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
2913 if(action
& ACTION_ADD_TIMED_BAN
)
2915 duration
= ParseInterval(argv
[2]);
2919 reply("CSMSG_DURATION_TOO_LOW");
2923 else if(duration
> (86400 * 365 * 2))
2925 reply("CSMSG_DURATION_TOO_HIGH");
2931 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2933 if(match_ircglobs(bData
->mask
, ban
))
2935 int exact
= !irccasecmp(bData
->mask
, ban
);
2937 /* The ban is redundant; there is already a ban
2938 with the same effect in place. */
2942 free(bData
->reason
);
2943 bData
->reason
= strdup(reason
);
2944 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
2946 reply("CSMSG_REASON_CHANGE", ban
);
2950 if(exact
&& bData
->expires
)
2954 /* If the ban matches an existing one exactly,
2955 extend the expiration time if the provided
2956 duration is longer. */
2957 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
2959 bData
->expires
= now
+ duration
;
2970 /* Delete the expiration timeq entry and
2971 requeue if necessary. */
2972 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
2975 timeq_add(bData
->expires
, expire_ban
, bData
);
2979 /* automated kickban */
2982 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
2984 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2990 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2997 if(match_ircglobs(ban
, bData
->mask
))
2999 /* The ban we are adding makes previously existing
3000 bans redundant; silently remove them. */
3001 del_channel_ban(bData
);
3005 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
);
3007 name
= ban
= strdup(bData
->mask
);
3011 /* WHAT DOES THIS DO?? -Rubin */
3012 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3014 extern const char *hidden_host_suffix
;
3015 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3017 unsigned int l1
, l2
;
3020 l2
= strlen(old_name
);
3023 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3025 new_mask
= malloc(MAXLEN
);
3026 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3028 name
= ban
= new_mask
;
3033 if(action
& ACTION_BAN
)
3035 unsigned int exists
;
3036 struct mod_chanmode
*change
;
3038 if(channel
->banlist
.used
>= MAXBANS
)
3041 reply("CSMSG_BANLIST_FULL", channel
->name
);
3046 exists
= ChannelBanExists(channel
, ban
);
3047 change
= mod_chanmode_alloc(victimCount
+ 1);
3048 for(n
= 0; n
< victimCount
; ++n
)
3050 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3051 change
->args
[n
].u
.member
= victims
[n
];
3055 change
->args
[n
].mode
= MODE_BAN
;
3056 change
->args
[n
++].u
.hostmask
= ban
;
3060 modcmd_chanmode_announce(change
);
3062 mod_chanmode_announce(chanserv
, channel
, change
);
3063 mod_chanmode_free(change
);
3065 if(exists
&& (action
== ACTION_BAN
))
3068 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3074 if(action
& ACTION_KICK
)
3076 char kick_reason
[MAXLEN
];
3077 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3079 for(n
= 0; n
< victimCount
; n
++)
3080 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3085 /* No response, since it was automated. */
3087 else if(action
& ACTION_ADD_BAN
)
3090 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3092 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3094 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3095 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3096 else if(action
& ACTION_BAN
)
3097 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3098 else if(action
& ACTION_KICK
&& victimCount
)
3099 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3105 static CHANSERV_FUNC(cmd_kickban
)
3107 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3110 static CHANSERV_FUNC(cmd_kick
)
3112 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3115 static CHANSERV_FUNC(cmd_ban
)
3117 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3120 static CHANSERV_FUNC(cmd_addban
)
3122 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
3125 static CHANSERV_FUNC(cmd_addtimedban
)
3127 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
3130 static struct mod_chanmode
*
3131 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3133 struct mod_chanmode
*change
;
3134 unsigned char *match
;
3135 unsigned int ii
, count
;
3137 match
= alloca(bans
->used
);
3140 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3142 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3149 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3151 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3158 change
= mod_chanmode_alloc(count
);
3159 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3163 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3164 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3166 assert(count
== change
->argc
);
3171 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3173 struct userNode
*actee
;
3179 /* may want to allow a comma delimited list of users... */
3180 if(!(actee
= GetUserH(argv
[1])))
3182 if(!is_ircmask(argv
[1]))
3184 reply("MSG_NICK_UNKNOWN", argv
[1]);
3188 mask
= strdup(argv
[1]);
3191 /* We don't sanitize the mask here because ircu
3193 if(action
& ACTION_UNBAN
)
3195 struct mod_chanmode
*change
;
3196 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3201 modcmd_chanmode_announce(change
);
3202 for(ii
= 0; ii
< change
->argc
; ++ii
)
3203 free((char*)change
->args
[ii
].u
.hostmask
);
3204 mod_chanmode_free(change
);
3209 if(action
& ACTION_DEL_BAN
)
3211 struct banData
*ban
, *next
;
3213 ban
= channel
->channel_info
->bans
;
3217 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3220 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3225 del_channel_ban(ban
);
3232 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3234 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3240 static CHANSERV_FUNC(cmd_unban
)
3242 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3245 static CHANSERV_FUNC(cmd_delban
)
3247 /* it doesn't necessarily have to remove the channel ban - may want
3248 to make that an option. */
3249 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3252 static CHANSERV_FUNC(cmd_unbanme
)
3254 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3255 long flags
= ACTION_UNBAN
;
3257 /* remove permanent bans if the user has the proper access. */
3258 if(uData
->access
>= UL_MANAGER
)
3259 flags
|= ACTION_DEL_BAN
;
3261 argv
[1] = user
->nick
;
3262 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3265 static CHANSERV_FUNC(cmd_unbanall
)
3267 struct mod_chanmode
*change
;
3270 if(!channel
->banlist
.used
)
3272 reply("CSMSG_NO_BANS", channel
->name
);
3276 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3277 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3279 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3280 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3282 modcmd_chanmode_announce(change
);
3283 for(ii
= 0; ii
< change
->argc
; ++ii
)
3284 free((char*)change
->args
[ii
].u
.hostmask
);
3285 mod_chanmode_free(change
);
3286 reply("CSMSG_BANS_REMOVED", channel
->name
);
3290 static CHANSERV_FUNC(cmd_open
)
3292 struct mod_chanmode
*change
;
3295 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3297 change
= mod_chanmode_alloc(0);
3298 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3299 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3300 && channel
->channel_info
->modes
.modes_set
)
3301 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3302 modcmd_chanmode_announce(change
);
3303 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3304 for(ii
= 0; ii
< change
->argc
; ++ii
)
3305 free((char*)change
->args
[ii
].u
.hostmask
);
3306 mod_chanmode_free(change
);
3310 static CHANSERV_FUNC(cmd_myaccess
)
3312 static struct string_buffer sbuf
;
3313 struct handle_info
*target_handle
;
3314 struct userData
*uData
;
3317 target_handle
= user
->handle_info
;
3318 else if(!IsHelping(user
))
3320 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3323 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3326 if(!target_handle
->channels
)
3328 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3332 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3333 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3335 struct chanData
*cData
= uData
->channel
;
3337 if(uData
->access
> UL_OWNER
)
3339 if(IsProtected(cData
)
3340 && (target_handle
!= user
->handle_info
)
3341 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3344 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3345 if(uData
->flags
!= USER_AUTO_OP
)
3346 string_buffer_append(&sbuf
, ',');
3347 if(IsUserSuspended(uData
))
3348 string_buffer_append(&sbuf
, 's');
3349 if(IsUserAutoOp(uData
))
3351 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3352 string_buffer_append(&sbuf
, 'o');
3353 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3354 string_buffer_append(&sbuf
, 'h');
3355 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3356 string_buffer_append(&sbuf
, 'v');
3358 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3359 string_buffer_append(&sbuf
, 'i');
3361 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3363 string_buffer_append_string(&sbuf
, ")]");
3364 string_buffer_append(&sbuf
, '\0');
3365 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3371 static CHANSERV_FUNC(cmd_access
)
3373 struct userNode
*target
;
3374 struct handle_info
*target_handle
;
3375 struct userData
*uData
;
3377 char prefix
[MAXLEN
];
3382 target_handle
= target
->handle_info
;
3384 else if((target
= GetUserH(argv
[1])))
3386 target_handle
= target
->handle_info
;
3388 else if(argv
[1][0] == '*')
3390 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3392 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3398 reply("MSG_NICK_UNKNOWN", argv
[1]);
3402 assert(target
|| target_handle
);
3404 if(target
== chanserv
)
3406 reply("CSMSG_IS_CHANSERV");
3414 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3419 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3422 reply("MSG_AUTHENTICATE");
3428 const char *epithet
= NULL
, *type
= NULL
;
3431 epithet
= chanserv_conf
.irc_operator_epithet
;
3434 else if(IsNetworkHelper(target
))
3436 epithet
= chanserv_conf
.network_helper_epithet
;
3437 type
= "network helper";
3439 else if(IsSupportHelper(target
))
3441 epithet
= chanserv_conf
.support_helper_epithet
;
3442 type
= "support helper";
3446 if(target_handle
->epithet
)
3447 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3449 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3451 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3455 sprintf(prefix
, "%s", target_handle
->handle
);
3458 if(!channel
->channel_info
)
3460 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3464 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3465 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3466 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3468 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3469 /* To prevent possible information leaks, only show infolines
3470 * if the requestor is in the channel or it's their own
3472 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3474 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3476 /* Likewise, only say it's suspended if the user has active
3477 * access in that channel or it's their own entry. */
3478 if(IsUserSuspended(uData
)
3479 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3480 || (user
->handle_info
== uData
->handle
)))
3482 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3487 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3493 /* This is never used... */
3495 zoot_list(struct listData
*list
)
3497 struct userData
*uData
;
3498 unsigned int start
, curr
, highest
, lowest
;
3499 struct helpfile_table tmp_table
;
3500 const char **temp
, *msg
;
3502 if(list
->table
.length
== 1)
3505 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
);
3507 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
));
3508 msg
= user_find_message(list
->user
, "MSG_NONE");
3509 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3511 tmp_table
.width
= list
->table
.width
;
3512 tmp_table
.flags
= list
->table
.flags
;
3513 list
->table
.contents
[0][0] = " ";
3514 highest
= list
->highest
;
3515 if(list
->lowest
!= 0)
3516 lowest
= list
->lowest
;
3517 else if(highest
< 100)
3520 lowest
= highest
- 100;
3521 for(start
= curr
= 1; curr
< list
->table
.length
; )
3523 uData
= list
->users
[curr
-1];
3524 list
->table
.contents
[curr
++][0] = " ";
3525 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3528 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
);
3530 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
));
3531 temp
= list
->table
.contents
[--start
];
3532 list
->table
.contents
[start
] = list
->table
.contents
[0];
3533 tmp_table
.contents
= list
->table
.contents
+ start
;
3534 tmp_table
.length
= curr
- start
;
3535 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3536 list
->table
.contents
[start
] = temp
;
3538 highest
= lowest
- 1;
3539 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3545 def_list(struct listData
*list
)
3549 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
);
3551 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
));
3552 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3553 if(list
->table
.length
== 1)
3555 msg
= user_find_message(list
->user
, "MSG_NONE");
3556 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3561 userData_access_comp(const void *arg_a
, const void *arg_b
)
3563 const struct userData
*a
= *(struct userData
**)arg_a
;
3564 const struct userData
*b
= *(struct userData
**)arg_b
;
3566 if(a
->access
!= b
->access
)
3567 res
= b
->access
- a
->access
;
3569 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3574 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3576 void (*send_list
)(struct listData
*);
3577 struct userData
*uData
;
3578 struct listData lData
;
3579 unsigned int matches
;
3583 lData
.bot
= cmd
->parent
->bot
;
3584 lData
.channel
= channel
;
3585 lData
.lowest
= lowest
;
3586 lData
.highest
= highest
;
3587 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3588 send_list
= def_list
;
3589 /* What does the following line do exactly?? */
3590 (void)zoot_list
; /* since it doesn't show user levels */
3592 /* this does nothing!! -rubin
3593 if(user->handle_info)
3595 switch(user->handle_info->userlist_style)
3597 case HI_STYLE_DEF: send_list = def_list; break;
3598 case HI_STYLE_ZOOT: send_list = def_list; break;
3603 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3605 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3607 if((uData
->access
< lowest
)
3608 || (uData
->access
> highest
)
3609 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3611 lData
.users
[matches
++] = uData
;
3613 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3615 lData
.table
.length
= matches
+1;
3616 lData
.table
.width
= 5;
3617 lData
.table
.flags
= TABLE_NO_FREE
;
3618 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3619 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3620 lData
.table
.contents
[0] = ary
;
3624 ary
[3] = "Last Seen";
3626 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3628 struct userData
*uData
= lData
.users
[matches
-1];
3629 char seen
[INTERVALLEN
];
3631 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3632 lData
.table
.contents
[matches
] = ary
;
3633 /* ary[0] = strtab(uData->access);*/
3634 ary
[0] = user_level_name_from_level(uData
->access
);
3635 /* TODO: replace above with func that returns static string
3636 * of userlevel for that level. eg OP/MANAGER etc. -rubin */
3637 ary
[1] = strtab(uData
->access
);
3638 ary
[2] = uData
->handle
->handle
;
3641 else if(!uData
->seen
)
3644 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3645 ary
[3] = strdup(ary
[3]);
3646 if(IsUserSuspended(uData
))
3647 ary
[4] = "Suspended";
3648 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3649 ary
[4] = "Vacation";
3654 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3656 free((char*)lData
.table
.contents
[matches
][3]);
3657 free(lData
.table
.contents
[matches
]);
3659 free(lData
.table
.contents
[0]);
3660 free(lData
.table
.contents
);
3664 static CHANSERV_FUNC(cmd_users
)
3666 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3669 static CHANSERV_FUNC(cmd_wlist
)
3671 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3674 static CHANSERV_FUNC(cmd_clist
)
3676 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3679 static CHANSERV_FUNC(cmd_mlist
)
3681 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3684 static CHANSERV_FUNC(cmd_olist
)
3686 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3689 static CHANSERV_FUNC(cmd_hlist
)
3691 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3694 static CHANSERV_FUNC(cmd_plist
)
3696 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3699 static CHANSERV_FUNC(cmd_bans
)
3701 struct helpfile_table tbl
;
3702 unsigned int matches
= 0, timed
= 0, ii
;
3703 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3704 const char *msg_never
, *triggered
, *expires
;
3705 struct banData
*ban
, **bans
;
3712 reply("CSMSG_BANS_HEADER", channel
->name
);
3713 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3715 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3717 if(search
&& !match_ircglobs(search
, ban
->mask
))
3719 bans
[matches
++] = ban
;
3724 tbl
.length
= matches
+ 1;
3725 tbl
.width
= 4 + timed
;
3727 tbl
.flags
= TABLE_NO_FREE
;
3728 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3729 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3730 tbl
.contents
[0][0] = "Mask";
3731 tbl
.contents
[0][1] = "Set By";
3732 tbl
.contents
[0][2] = "Triggered";
3735 tbl
.contents
[0][3] = "Expires";
3736 tbl
.contents
[0][4] = "Reason";
3739 tbl
.contents
[0][3] = "Reason";
3742 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3744 free(tbl
.contents
[0]);
3749 msg_never
= user_find_message(user
, "MSG_NEVER");
3750 for(ii
= 0; ii
< matches
; )
3756 else if(ban
->expires
)
3757 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3759 expires
= msg_never
;
3762 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3764 triggered
= msg_never
;
3766 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3767 tbl
.contents
[ii
][0] = ban
->mask
;
3768 tbl
.contents
[ii
][1] = ban
->owner
;
3769 tbl
.contents
[ii
][2] = strdup(triggered
);
3772 tbl
.contents
[ii
][3] = strdup(expires
);
3773 tbl
.contents
[ii
][4] = ban
->reason
;
3776 tbl
.contents
[ii
][3] = ban
->reason
;
3778 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3779 /* reply("MSG_MATCH_COUNT", matches); */
3780 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3782 free((char*)tbl
.contents
[ii
][2]);
3784 free((char*)tbl
.contents
[ii
][3]);
3785 free(tbl
.contents
[ii
]);
3787 free(tbl
.contents
[0]);
3794 * return + if the user does NOT have the right to set the topic, and
3795 * the topic is changed.
3798 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3800 struct chanData
*cData
= channel
->channel_info
;
3801 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3803 else if(cData
->topic
)
3804 return irccasecmp(new_topic
, cData
->topic
);
3811 * Makes a givin topic fit into a givin topic mask and returns
3814 * topic_mask - the mask to conform to
3815 * topic - the topic to make conform
3816 * new_topic - the pre-allocated char* to put the new topic into
3818 * modifies: new_topic
3821 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
3823 //char *topic_mask = cData->topic_mask;
3825 int pos
=0, starpos
=-1, dpos
=0, len
;
3827 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3834 strcpy(new_topic
, "");
3837 len
= strlen(topic
);
3838 if((dpos
+ len
) > TOPICLEN
)
3839 len
= TOPICLEN
+ 1 - dpos
;
3840 memcpy(new_topic
+dpos
, topic
, len
);
3844 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
3845 default: new_topic
[dpos
++] = tchar
; break;
3848 if((dpos
> TOPICLEN
) || tchar
)
3850 strcpy(new_topic
, "");
3853 new_topic
[dpos
] = 0;
3857 static CHANSERV_FUNC(cmd_topic
)
3859 struct chanData
*cData
;
3862 cData
= channel
->channel_info
;
3867 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
3868 reply("CSMSG_TOPIC_SET", cData
->topic
);
3872 reply("CSMSG_NO_TOPIC", channel
->name
);
3876 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
3877 /* If they say "!topic *", use an empty topic. */
3878 if((topic
[0] == '*') && (topic
[1] == 0))
3881 if(bad_topic(channel
, user
, topic
))
3883 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
3888 /* If there is a topicmask set, and the new topic doesnt match, make it */
3889 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
3891 char *topic_mask
= cData
->topic_mask
;
3892 char new_topic
[TOPICLEN
+1];
3894 /* make a new topic fitting mask */
3895 conform_topic(topic_mask
, topic
, new_topic
);
3898 /* Topic couldnt fit into mask, was too long */
3899 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
3900 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
3903 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
3905 else /* No mask set, just set the topic */
3906 SetChannelTopic(channel
, chanserv
, topic
, 1);
3909 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
3911 /* Grab the topic and save it as the default topic. */
3913 cData
->topic
= strdup(channel
->topic
);
3919 static CHANSERV_FUNC(cmd_mode
)
3921 struct mod_chanmode
*change
;
3925 change
= &channel
->channel_info
->modes
;
3926 if(change
->modes_set
|| change
->modes_clear
) {
3927 modcmd_chanmode_announce(change
);
3928 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
3930 reply("CSMSG_NO_MODES", channel
->name
);
3934 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
3937 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
3941 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3942 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
3945 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
3946 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
3950 modcmd_chanmode_announce(change
);
3951 mod_chanmode_free(change
);
3952 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
3956 static CHANSERV_FUNC(cmd_invite
)
3958 struct userData
*uData
;
3959 struct userNode
*invite
;
3961 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3965 if(!(invite
= GetUserH(argv
[1])))
3967 reply("MSG_NICK_UNKNOWN", argv
[1]);
3974 if(GetUserMode(channel
, invite
))
3976 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
3984 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
3985 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
3988 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
3990 irc_invite(chanserv
, invite
, channel
);
3992 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
3997 static CHANSERV_FUNC(cmd_inviteme
)
3999 if(GetUserMode(channel
, user
))
4001 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4004 if(channel
->channel_info
4005 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4007 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4010 irc_invite(cmd
->parent
->bot
, user
, channel
);
4015 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4018 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4020 /* We display things based on two dimensions:
4021 * - Issue time: present or absent
4022 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4023 * (in order of precedence, so something both expired and revoked
4024 * only counts as revoked)
4026 combo
= (suspended
->issued
? 4 : 0)
4027 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4029 case 0: /* no issue time, indefinite expiration */
4030 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4032 case 1: /* no issue time, expires in future */
4033 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4034 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4036 case 2: /* no issue time, expired */
4037 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4038 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4040 case 3: /* no issue time, revoked */
4041 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4042 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4044 case 4: /* issue time set, indefinite expiration */
4045 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4046 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4048 case 5: /* issue time set, expires in future */
4049 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4050 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4051 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4053 case 6: /* issue time set, expired */
4054 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4055 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4056 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4058 case 7: /* issue time set, revoked */
4059 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4060 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4061 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4064 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4069 static CHANSERV_FUNC(cmd_info
)
4071 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4072 struct userData
*uData
, *owner
;
4073 struct chanData
*cData
;
4074 struct do_not_register
*dnr
;
4079 cData
= channel
->channel_info
;
4080 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4083 uData
= GetChannelUser(cData
, user
->handle_info
);
4084 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4086 mod_chanmode_format(&cData
->modes
, modes
);
4087 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4088 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4091 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4095 note
= iter_data(it
);
4096 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4099 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4100 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4103 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4104 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4105 if(owner
->access
== UL_OWNER
)
4106 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4107 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4108 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
4109 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4110 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4112 privileged
= IsStaff(user
);
4113 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4114 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4116 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4117 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4119 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4121 struct suspended
*suspended
;
4122 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4123 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4124 show_suspension_info(cmd
, user
, suspended
);
4126 else if(IsSuspended(cData
))
4128 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4129 show_suspension_info(cmd
, user
, cData
->suspended
);
4131 reply("CSMSG_CHANNEL_END");
4135 static CHANSERV_FUNC(cmd_netinfo
)
4137 extern time_t boot_time
;
4138 extern unsigned long burst_length
;
4139 char interval
[INTERVALLEN
];
4141 reply("CSMSG_NETWORK_INFO");
4142 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4143 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4144 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4145 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4146 reply("CSMSG_NETWORK_BANS", banCount
);
4147 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4148 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4149 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4154 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4156 struct helpfile_table table
;
4158 struct userNode
*user
;
4163 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4164 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4165 for(nn
=0; nn
<list
->used
; nn
++)
4167 user
= list
->list
[nn
];
4168 if(user
->modes
& skip_flags
)
4172 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4175 nick
= alloca(strlen(user
->nick
)+3);
4176 sprintf(nick
, "(%s)", user
->nick
);
4180 table
.contents
[table
.length
][0] = nick
;
4183 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4186 static CHANSERV_FUNC(cmd_ircops
)
4188 reply("CSMSG_STAFF_OPERS");
4189 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4193 static CHANSERV_FUNC(cmd_helpers
)
4195 reply("CSMSG_STAFF_HELPERS");
4196 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4200 static CHANSERV_FUNC(cmd_staff
)
4202 reply("CSMSG_NETWORK_STAFF");
4203 cmd_ircops(CSFUNC_ARGS
);
4204 cmd_helpers(CSFUNC_ARGS
);
4208 static CHANSERV_FUNC(cmd_peek
)
4210 struct modeNode
*mn
;
4211 char modes
[MODELEN
];
4213 struct helpfile_table table
;
4215 irc_make_chanmode(channel
, modes
);
4217 reply("CSMSG_PEEK_INFO", channel
->name
);
4219 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4220 reply("CSMSG_PEEK_MODES", modes
);
4221 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4225 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4226 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4227 for(n
= 0; n
< channel
->members
.used
; n
++)
4229 mn
= channel
->members
.list
[n
];
4230 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4232 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4233 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4238 reply("CSMSG_PEEK_OPS");
4239 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4242 reply("CSMSG_PEEK_NO_OPS");
4243 reply("CSMSG_PEEK_END");
4247 static MODCMD_FUNC(cmd_wipeinfo
)
4249 struct handle_info
*victim
;
4250 struct userData
*ud
, *actor
;
4253 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4254 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4256 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4258 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4261 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4263 reply("MSG_USER_OUTRANKED", victim
->handle
);
4269 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4273 static CHANSERV_FUNC(cmd_resync
)
4275 struct mod_chanmode
*changes
;
4276 struct chanData
*cData
= channel
->channel_info
;
4277 unsigned int ii
, used
;
4279 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4280 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4282 struct modeNode
*mn
= channel
->members
.list
[ii
];
4283 struct userData
*uData
;
4285 if(IsService(mn
->user
))
4288 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4289 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4291 if(!(mn
->modes
& MODE_CHANOP
))
4293 changes
->args
[used
].mode
= MODE_CHANOP
;
4294 changes
->args
[used
++].u
.member
= mn
;
4297 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4299 if(!(mn
->modes
& MODE_HALFOP
))
4301 changes
->args
[used
].mode
= MODE_HALFOP
;
4302 changes
->args
[used
++].u
.member
= mn
;
4304 if(mn
->modes
& MODE_CHANOP
)
4306 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4307 changes
->args
[used
++].u
.member
= mn
;
4309 if(mn
->modes
& MODE_VOICE
)
4311 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4312 changes
->args
[used
++].u
.member
= mn
;
4315 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4317 if(mn
->modes
& MODE_CHANOP
)
4319 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4320 changes
->args
[used
++].u
.member
= mn
;
4322 if(mn
->modes
& MODE_HALFOP
)
4324 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4325 changes
->args
[used
++].u
.member
= mn
;
4327 if(!(mn
->modes
& MODE_VOICE
))
4329 changes
->args
[used
].mode
= MODE_VOICE
;
4330 changes
->args
[used
++].u
.member
= mn
;
4337 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4338 changes
->args
[used
++].u
.member
= mn
;
4342 changes
->argc
= used
;
4343 modcmd_chanmode_announce(changes
);
4344 mod_chanmode_free(changes
);
4345 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4349 static CHANSERV_FUNC(cmd_seen
)
4351 struct userData
*uData
;
4352 struct handle_info
*handle
;
4353 char seen
[INTERVALLEN
];
4357 if(!irccasecmp(argv
[1], chanserv
->nick
))
4359 reply("CSMSG_IS_CHANSERV");
4363 if(!(handle
= get_handle_info(argv
[1])))
4365 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4369 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4371 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4376 reply("CSMSG_USER_PRESENT", handle
->handle
);
4377 else if(uData
->seen
)
4378 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4380 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4382 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4383 reply("CSMSG_USER_VACATION", handle
->handle
);
4388 static MODCMD_FUNC(cmd_names
)
4390 struct userNode
*targ
;
4391 struct userData
*targData
;
4392 unsigned int ii
, pos
;
4395 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4397 targ
= channel
->members
.list
[ii
]->user
;
4398 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4401 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4404 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4408 if(IsUserSuspended(targData
))
4410 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4413 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4414 reply("CSMSG_END_NAMES", channel
->name
);
4419 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4421 switch(ntype
->visible_type
)
4423 case NOTE_VIS_ALL
: return 1;
4424 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4425 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4430 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4432 struct userData
*uData
;
4434 switch(ntype
->set_access_type
)
4436 case NOTE_SET_CHANNEL_ACCESS
:
4437 if(!user
->handle_info
)
4439 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4441 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4442 case NOTE_SET_CHANNEL_SETTER
:
4443 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4444 case NOTE_SET_PRIVILEGED
: default:
4445 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4449 static CHANSERV_FUNC(cmd_note
)
4451 struct chanData
*cData
;
4453 struct note_type
*ntype
;
4455 cData
= channel
->channel_info
;
4458 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4462 /* If no arguments, show all visible notes for the channel. */
4468 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4470 note
= iter_data(it
);
4471 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4474 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4475 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4478 reply("CSMSG_NOTELIST_END", channel
->name
);
4480 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4482 /* If one argument, show the named note. */
4485 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4486 && note_type_visible_to_user(cData
, note
->type
, user
))
4488 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4490 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4491 && note_type_visible_to_user(NULL
, ntype
, user
))
4493 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4498 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4502 /* Assume they're trying to set a note. */
4506 ntype
= dict_find(note_types
, argv
[1], NULL
);
4509 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4512 else if(note_type_settable_by_user(channel
, ntype
, user
))
4514 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4515 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4516 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4517 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4518 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4520 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4522 /* The note is viewable to staff only, so return 0
4523 to keep the invocation from getting logged (or
4524 regular users can see it in !events). */
4530 reply("CSMSG_NO_ACCESS");
4537 static CHANSERV_FUNC(cmd_delnote
)
4542 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4543 || !note_type_settable_by_user(channel
, note
->type
, user
))
4545 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4548 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4549 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4553 static CHANSERV_FUNC(cmd_events
)
4555 struct logSearch discrim
;
4556 struct logReport report
;
4557 unsigned int matches
, limit
;
4559 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4560 if(limit
< 1 || limit
> 200)
4563 memset(&discrim
, 0, sizeof(discrim
));
4564 discrim
.masks
.bot
= chanserv
;
4565 discrim
.masks
.channel_name
= channel
->name
;
4567 discrim
.masks
.command
= argv
[2];
4568 discrim
.limit
= limit
;
4569 discrim
.max_time
= INT_MAX
;
4570 discrim
.severities
= 1 << LOG_COMMAND
;
4571 report
.reporter
= chanserv
;
4573 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4575 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4577 reply("MSG_MATCH_COUNT", matches
);
4579 reply("MSG_NO_MATCHES");
4583 static CHANSERV_FUNC(cmd_say
)
4589 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4590 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4592 else if(GetUserH(argv
[1]))
4595 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4596 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4600 reply("MSG_NOT_TARGET_NAME");
4606 static CHANSERV_FUNC(cmd_emote
)
4612 /* CTCP is so annoying. */
4613 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4614 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4616 else if(GetUserH(argv
[1]))
4618 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4619 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4623 reply("MSG_NOT_TARGET_NAME");
4629 struct channelList
*
4630 chanserv_support_channels(void)
4632 return &chanserv_conf
.support_channels
;
4635 static CHANSERV_FUNC(cmd_expire
)
4637 int channel_count
= registered_channels
;
4638 expire_channels(NULL
);
4639 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4644 chanserv_expire_suspension(void *data
)
4646 struct suspended
*suspended
= data
;
4647 struct chanNode
*channel
;
4649 if(!suspended
->expires
|| (now
< suspended
->expires
))
4650 suspended
->revoked
= now
;
4651 channel
= suspended
->cData
->channel
;
4652 suspended
->cData
->channel
= channel
;
4653 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4654 if(!IsOffChannel(suspended
->cData
))
4656 struct mod_chanmode change
;
4657 mod_chanmode_init(&change
);
4659 change
.args
[0].mode
= MODE_CHANOP
;
4660 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4661 mod_chanmode_announce(chanserv
, channel
, &change
);
4665 static CHANSERV_FUNC(cmd_csuspend
)
4667 struct suspended
*suspended
;
4668 char reason
[MAXLEN
];
4669 time_t expiry
, duration
;
4670 struct userData
*uData
;
4674 if(IsProtected(channel
->channel_info
))
4676 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4680 if(argv
[1][0] == '!')
4682 else if(IsSuspended(channel
->channel_info
))
4684 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4685 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4689 if(!strcmp(argv
[1], "0"))
4691 else if((duration
= ParseInterval(argv
[1])))
4692 expiry
= now
+ duration
;
4695 reply("MSG_INVALID_DURATION", argv
[1]);
4699 unsplit_string(argv
+ 2, argc
- 2, reason
);
4701 suspended
= calloc(1, sizeof(*suspended
));
4702 suspended
->revoked
= 0;
4703 suspended
->issued
= now
;
4704 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4705 suspended
->expires
= expiry
;
4706 suspended
->reason
= strdup(reason
);
4707 suspended
->cData
= channel
->channel_info
;
4708 suspended
->previous
= suspended
->cData
->suspended
;
4709 suspended
->cData
->suspended
= suspended
;
4711 if(suspended
->expires
)
4712 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4714 if(IsSuspended(channel
->channel_info
))
4716 suspended
->previous
->revoked
= now
;
4717 if(suspended
->previous
->expires
)
4718 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4719 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4720 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4724 /* Mark all users in channel as absent. */
4725 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4734 /* Mark the channel as suspended, then part. */
4735 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4736 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4737 reply("CSMSG_SUSPENDED", channel
->name
);
4738 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4739 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4744 static CHANSERV_FUNC(cmd_cunsuspend
)
4746 struct suspended
*suspended
;
4747 char message
[MAXLEN
];
4749 if(!IsSuspended(channel
->channel_info
))
4751 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4755 suspended
= channel
->channel_info
->suspended
;
4757 /* Expire the suspension and join ChanServ to the channel. */
4758 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4759 chanserv_expire_suspension(suspended
);
4760 reply("CSMSG_UNSUSPENDED", channel
->name
);
4761 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4762 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4766 typedef struct chanservSearch
4774 unsigned long flags
;
4778 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4781 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4786 search
= malloc(sizeof(struct chanservSearch
));
4787 memset(search
, 0, sizeof(*search
));
4790 for(i
= 0; i
< argc
; i
++)
4792 /* Assume all criteria require arguments. */
4795 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4799 if(!irccasecmp(argv
[i
], "name"))
4800 search
->name
= argv
[++i
];
4801 else if(!irccasecmp(argv
[i
], "registrar"))
4802 search
->registrar
= argv
[++i
];
4803 else if(!irccasecmp(argv
[i
], "unvisited"))
4804 search
->unvisited
= ParseInterval(argv
[++i
]);
4805 else if(!irccasecmp(argv
[i
], "registered"))
4806 search
->registered
= ParseInterval(argv
[++i
]);
4807 else if(!irccasecmp(argv
[i
], "flags"))
4810 if(!irccasecmp(argv
[i
], "nodelete"))
4811 search
->flags
|= CHANNEL_NODELETE
;
4812 else if(!irccasecmp(argv
[i
], "suspended"))
4813 search
->flags
|= CHANNEL_SUSPENDED
;
4816 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4820 else if(!irccasecmp(argv
[i
], "limit"))
4821 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4824 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4829 if(search
->name
&& !strcmp(search
->name
, "*"))
4831 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4832 search
->registrar
= 0;
4841 chanserv_channel_match(struct chanData
*channel
, search_t search
)
4843 const char *name
= channel
->channel
->name
;
4844 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
4845 (search
->registrar
&& !channel
->registrar
) ||
4846 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
4847 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
4848 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
4849 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
4856 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
4858 struct chanData
*channel
;
4859 unsigned int matches
= 0;
4861 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
4863 if(!chanserv_channel_match(channel
, search
))
4873 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
4878 search_print(struct chanData
*channel
, void *data
)
4880 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
4883 static CHANSERV_FUNC(cmd_search
)
4886 unsigned int matches
;
4887 channel_search_func action
;
4891 if(!irccasecmp(argv
[1], "count"))
4892 action
= search_count
;
4893 else if(!irccasecmp(argv
[1], "print"))
4894 action
= search_print
;
4897 reply("CSMSG_ACTION_INVALID", argv
[1]);
4901 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
4905 if(action
== search_count
)
4906 search
->limit
= INT_MAX
;
4908 if(action
== search_print
)
4910 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4914 matches
= chanserv_channel_search(search
, action
, user
);
4917 reply("MSG_MATCH_COUNT", matches
);
4919 reply("MSG_NO_MATCHES");
4925 static CHANSERV_FUNC(cmd_unvisited
)
4927 struct chanData
*cData
;
4928 time_t interval
= chanserv_conf
.channel_expire_delay
;
4929 char buffer
[INTERVALLEN
];
4930 unsigned int limit
= 25, matches
= 0;
4934 interval
= ParseInterval(argv
[1]);
4936 limit
= atoi(argv
[2]);
4939 intervalString(buffer
, interval
, user
->handle_info
);
4940 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
4942 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
4944 if((now
- cData
->visited
) < interval
)
4947 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
4948 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
4955 static MODCMD_FUNC(chan_opt_defaulttopic
)
4961 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4963 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4967 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
4969 free(channel
->channel_info
->topic
);
4970 if(topic
[0] == '*' && topic
[1] == 0)
4972 topic
= channel
->channel_info
->topic
= NULL
;
4976 topic
= channel
->channel_info
->topic
= strdup(topic
);
4977 if(channel
->channel_info
->topic_mask
4978 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
4979 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4981 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
4984 if(channel
->channel_info
->topic
)
4985 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
4987 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
4991 static MODCMD_FUNC(chan_opt_topicmask
)
4995 struct chanData
*cData
= channel
->channel_info
;
4998 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5000 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5004 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5006 if(cData
->topic_mask
)
5007 free(cData
->topic_mask
);
5008 if(mask
[0] == '*' && mask
[1] == 0)
5010 cData
->topic_mask
= 0;
5014 cData
->topic_mask
= strdup(mask
);
5016 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5017 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5018 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5022 if(channel
->channel_info
->topic_mask
)
5023 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5025 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5029 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5033 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5037 if(greeting
[0] == '*' && greeting
[1] == 0)
5041 unsigned int length
= strlen(greeting
);
5042 if(length
> chanserv_conf
.greeting_length
)
5044 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5047 *data
= strdup(greeting
);
5056 reply(name
, user_find_message(user
, "MSG_NONE"));
5060 static MODCMD_FUNC(chan_opt_greeting
)
5062 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5065 static MODCMD_FUNC(chan_opt_usergreeting
)
5067 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5070 static MODCMD_FUNC(chan_opt_modes
)
5072 struct mod_chanmode
*new_modes
;
5073 char modes
[MODELEN
];
5077 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5079 reply("CSMSG_NO_ACCESS");
5082 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5084 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5086 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5088 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5091 else if(new_modes
->argc
> 1)
5093 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5094 mod_chanmode_free(new_modes
);
5099 channel
->channel_info
->modes
= *new_modes
;
5100 modcmd_chanmode_announce(new_modes
);
5101 mod_chanmode_free(new_modes
);
5105 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5107 reply("CSMSG_SET_MODES", modes
);
5109 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5113 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5115 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5117 struct chanData
*cData
= channel
->channel_info
;
5122 /* Set flag according to value. */
5123 if(enabled_string(argv
[1]))
5125 cData
->flags
|= mask
;
5128 else if(disabled_string(argv
[1]))
5130 cData
->flags
&= ~mask
;
5135 reply("MSG_INVALID_BINARY", argv
[1]);
5141 /* Find current option value. */
5142 value
= (cData
->flags
& mask
) ? 1 : 0;
5146 reply(name
, user_find_message(user
, "MSG_ON"));
5148 reply(name
, user_find_message(user
, "MSG_OFF"));
5152 static MODCMD_FUNC(chan_opt_nodelete
)
5154 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5156 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5160 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5163 static MODCMD_FUNC(chan_opt_dynlimit
)
5165 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5168 static MODCMD_FUNC(chan_opt_offchannel
)
5170 struct chanData
*cData
= channel
->channel_info
;
5175 /* Set flag according to value. */
5176 if(enabled_string(argv
[1]))
5178 if(!IsOffChannel(cData
))
5179 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5180 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5183 else if(disabled_string(argv
[1]))
5185 if(IsOffChannel(cData
))
5187 struct mod_chanmode change
;
5188 mod_chanmode_init(&change
);
5190 change
.args
[0].mode
= MODE_CHANOP
;
5191 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5192 mod_chanmode_announce(chanserv
, channel
, &change
);
5194 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5199 reply("MSG_INVALID_BINARY", argv
[1]);
5205 /* Find current option value. */
5206 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5210 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5212 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5216 static MODCMD_FUNC(chan_opt_defaults
)
5218 struct userData
*uData
;
5219 struct chanData
*cData
;
5220 const char *confirm
;
5221 enum levelOption lvlOpt
;
5222 enum charOption chOpt
;
5224 cData
= channel
->channel_info
;
5225 uData
= GetChannelUser(cData
, user
->handle_info
);
5226 if(!uData
|| (uData
->access
< UL_OWNER
))
5228 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5231 confirm
= make_confirmation_string(uData
);
5232 if((argc
< 2) || strcmp(argv
[1], confirm
))
5234 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5237 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5238 cData
->modes
= chanserv_conf
.default_modes
;
5239 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5240 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5241 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5242 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5243 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5248 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5250 struct chanData
*cData
= channel
->channel_info
;
5251 struct userData
*uData
;
5252 unsigned short value
;
5256 if(!check_user_level(channel
, user
, option
, 1, 1))
5258 reply("CSMSG_CANNOT_SET");
5261 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5262 if(!value
&& strcmp(argv
[1], "0"))
5264 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5267 uData
= GetChannelUser(cData
, user
->handle_info
);
5268 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5270 reply("CSMSG_BAD_SETLEVEL");
5275 /* removing these level sets..
5277 if(value > cData->lvlOpts[lvlGiveOps])
5279 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5283 case lvlGiveHalfOps:
5284 if(value < cData->lvlOpts[lvlGiveVoice])
5286 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5291 if(value < cData->lvlOpts[lvlGiveVoice])
5293 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5299 /* This test only applies to owners, since non-owners
5300 * trying to set an option to above their level get caught
5301 * by the CSMSG_BAD_SETLEVEL test above.
5303 if(value
> uData
->access
)
5305 reply("CSMSG_BAD_SETTERS");
5312 cData
->lvlOpts
[option
] = value
;
5314 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5318 static MODCMD_FUNC(chan_opt_enfops
)
5320 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5323 static MODCMD_FUNC(chan_opt_enfhalfops
)
5325 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5328 static MODCMD_FUNC(chan_opt_giveops)
5330 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5333 static MODCMD_FUNC(chan_opt_givehalfops)
5335 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5338 static MODCMD_FUNC(chan_opt_enfmodes
)
5340 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5343 static MODCMD_FUNC(chan_opt_enftopic
)
5345 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5348 static MODCMD_FUNC(chan_opt_pubcmd
)
5350 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5353 static MODCMD_FUNC(chan_opt_setters
)
5355 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5358 static MODCMD_FUNC(chan_opt_ctcpusers
)
5360 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5363 static MODCMD_FUNC(chan_opt_userinfo
)
5365 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5369 static MODCMD_FUNC(chan_opt_givevoice)
5371 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5375 static MODCMD_FUNC(chan_opt_topicsnarf
)
5377 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5380 static MODCMD_FUNC(chan_opt_inviteme
)
5382 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5386 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5388 struct chanData
*cData
= channel
->channel_info
;
5389 int count
= charOptions
[option
].count
, index
;
5393 index
= atoi(argv
[1]);
5395 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5397 reply("CSMSG_INVALID_NUMERIC", index
);
5398 /* Show possible values. */
5399 for(index
= 0; index
< count
; index
++)
5400 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5404 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5408 /* Find current option value. */
5411 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5415 /* Somehow, the option value is corrupt; reset it to the default. */
5416 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5421 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5425 static MODCMD_FUNC(chan_opt_protect
)
5427 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5430 static MODCMD_FUNC(chan_opt_toys
)
5432 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5435 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5437 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5440 static MODCMD_FUNC(chan_opt_topicrefresh
)
5442 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5445 static struct svccmd_list set_shows_list
;
5448 handle_svccmd_unbind(struct svccmd
*target
) {
5450 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5451 if(target
== set_shows_list
.list
[ii
])
5452 set_shows_list
.used
= 0;
5455 static CHANSERV_FUNC(cmd_set
)
5457 struct svccmd
*subcmd
;
5461 /* Check if we need to (re-)initialize set_shows_list. */
5462 if(!set_shows_list
.used
)
5464 if(!set_shows_list
.size
)
5466 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5467 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5469 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5471 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5472 sprintf(buf
, "%s %s", argv
[0], name
);
5473 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5476 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5479 svccmd_list_append(&set_shows_list
, subcmd
);
5485 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5487 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5489 subcmd
= set_shows_list
.list
[ii
];
5490 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5492 reply("CSMSG_CHANNEL_OPTIONS_END");
5496 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5497 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5500 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5503 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5505 reply("CSMSG_NO_ACCESS");
5509 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5513 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5515 struct userData
*uData
;
5517 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5520 reply("CSMSG_NOT_USER", channel
->name
);
5526 /* Just show current option value. */
5528 else if(enabled_string(argv
[1]))
5530 uData
->flags
|= mask
;
5532 else if(disabled_string(argv
[1]))
5534 uData
->flags
&= ~mask
;
5538 reply("MSG_INVALID_BINARY", argv
[1]);
5542 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5546 static MODCMD_FUNC(user_opt_noautoop
)
5548 struct userData
*uData
;
5550 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5553 reply("CSMSG_NOT_USER", channel
->name
);
5556 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5557 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5559 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5560 /* TODO: add halfops error message? or is the op one generic enough? */
5563 static MODCMD_FUNC(user_opt_autoinvite
)
5565 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5568 static MODCMD_FUNC(user_opt_info
)
5570 struct userData
*uData
;
5573 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5577 /* If they got past the command restrictions (which require access)
5578 * but fail this test, we have some fool with security override on.
5580 reply("CSMSG_NOT_USER", channel
->name
);
5587 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5588 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5590 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5593 bp
= strcspn(infoline
, "\001");
5596 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5601 if(infoline
[0] == '*' && infoline
[1] == 0)
5604 uData
->info
= strdup(infoline
);
5607 reply("CSMSG_USET_INFO", uData
->info
);
5609 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5613 struct svccmd_list uset_shows_list
;
5615 static CHANSERV_FUNC(cmd_uset
)
5617 struct svccmd
*subcmd
;
5621 /* Check if we need to (re-)initialize uset_shows_list. */
5622 if(!uset_shows_list
.used
)
5626 "NoAutoOp", "AutoInvite", "Info"
5629 if(!uset_shows_list
.size
)
5631 uset_shows_list
.size
= ArrayLength(options
);
5632 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5634 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5636 const char *name
= options
[ii
];
5637 sprintf(buf
, "%s %s", argv
[0], name
);
5638 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5641 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5644 svccmd_list_append(&uset_shows_list
, subcmd
);
5650 /* Do this so options are presented in a consistent order. */
5651 reply("CSMSG_USER_OPTIONS");
5652 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5653 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5657 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5658 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5661 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5665 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5668 static CHANSERV_FUNC(cmd_giveownership
)
5670 struct handle_info
*new_owner_hi
;
5671 struct userData
*new_owner
, *curr_user
;
5672 struct chanData
*cData
= channel
->channel_info
;
5673 struct do_not_register
*dnr
;
5675 unsigned short co_access
;
5676 char reason
[MAXLEN
];
5679 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5680 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5681 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5683 struct userData
*owner
= NULL
;
5684 for(curr_user
= channel
->channel_info
->users
;
5686 curr_user
= curr_user
->next
)
5688 if(curr_user
->access
!= UL_OWNER
)
5692 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5699 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5701 char delay
[INTERVALLEN
];
5702 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5703 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5706 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5708 if(new_owner_hi
== user
->handle_info
)
5710 reply("CSMSG_NO_TRANSFER_SELF");
5713 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5718 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5722 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5726 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5728 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5731 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5732 if(!IsHelping(user
))
5733 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5735 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5738 if(new_owner
->access
>= UL_COOWNER
)
5739 co_access
= new_owner
->access
;
5741 co_access
= UL_COOWNER
;
5742 new_owner
->access
= UL_OWNER
;
5744 curr_user
->access
= co_access
;
5745 cData
->ownerTransfer
= now
;
5746 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5747 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5748 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5752 static CHANSERV_FUNC(cmd_suspend
)
5754 struct handle_info
*hi
;
5755 struct userData
*self
, *target
;
5758 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5759 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5760 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5762 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5765 if(target
->access
>= self
->access
)
5767 reply("MSG_USER_OUTRANKED", hi
->handle
);
5770 if(target
->flags
& USER_SUSPENDED
)
5772 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5777 target
->present
= 0;
5780 target
->flags
|= USER_SUSPENDED
;
5781 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5785 static CHANSERV_FUNC(cmd_unsuspend
)
5787 struct handle_info
*hi
;
5788 struct userData
*self
, *target
;
5791 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5792 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5793 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5795 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5798 if(target
->access
>= self
->access
)
5800 reply("MSG_USER_OUTRANKED", hi
->handle
);
5803 if(!(target
->flags
& USER_SUSPENDED
))
5805 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5808 target
->flags
&= ~USER_SUSPENDED
;
5809 scan_user_presence(target
, NULL
);
5810 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5814 static MODCMD_FUNC(cmd_deleteme
)
5816 struct handle_info
*hi
;
5817 struct userData
*target
;
5818 const char *confirm_string
;
5819 unsigned short access
;
5822 hi
= user
->handle_info
;
5823 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5825 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5828 if(target
->access
== UL_OWNER
)
5830 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5833 confirm_string
= make_confirmation_string(target
);
5834 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
5836 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
5839 access
= target
->access
;
5840 channel_name
= strdup(channel
->name
);
5841 del_channel_user(target
, 1);
5842 reply("CSMSG_DELETED_YOU", access
, channel_name
);
5848 chanserv_refresh_topics(UNUSED_ARG(void *data
))
5850 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
5851 struct chanData
*cData
;
5854 for(cData
= channelList
; cData
; cData
= cData
->next
)
5856 if(IsSuspended(cData
))
5858 opt
= cData
->chOpts
[chTopicRefresh
];
5861 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
5864 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
5865 cData
->last_refresh
= refresh_num
;
5867 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
5870 static CHANSERV_FUNC(cmd_unf
)
5874 char response
[MAXLEN
];
5875 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
5876 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5877 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5880 reply("CSMSG_UNF_RESPONSE");
5884 static CHANSERV_FUNC(cmd_ping
)
5888 char response
[MAXLEN
];
5889 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
5890 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5891 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5894 reply("CSMSG_PING_RESPONSE");
5898 static CHANSERV_FUNC(cmd_wut
)
5902 char response
[MAXLEN
];
5903 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
5904 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5905 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5908 reply("CSMSG_WUT_RESPONSE");
5912 static CHANSERV_FUNC(cmd_8ball
)
5914 unsigned int i
, j
, accum
;
5919 for(i
=1; i
<argc
; i
++)
5920 for(j
=0; argv
[i
][j
]; j
++)
5921 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
5922 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
5925 char response
[MAXLEN
];
5926 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
5927 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5930 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
5934 static CHANSERV_FUNC(cmd_d
)
5936 unsigned long sides
, count
, modifier
, ii
, total
;
5937 char response
[MAXLEN
], *sep
;
5941 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
5951 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
5952 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
5956 else if((sep
[0] == '-') && isdigit(sep
[1]))
5957 modifier
= strtoul(sep
, NULL
, 10);
5958 else if((sep
[0] == '+') && isdigit(sep
[1]))
5959 modifier
= strtoul(sep
+1, NULL
, 10);
5966 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
5971 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
5974 for(total
= ii
= 0; ii
< count
; ++ii
)
5975 total
+= (rand() % sides
) + 1;
5978 if((count
> 1) || modifier
)
5980 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
5981 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
5985 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
5986 sprintf(response
, fmt
, total
, sides
);
5989 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5991 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5995 static CHANSERV_FUNC(cmd_huggle
)
5997 /* CTCP must be via PRIVMSG, never notice */
5999 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6001 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6005 static CHANSERV_FUNC(cmd_calc
)
6007 char response
[MAXLEN
];
6010 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6013 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6015 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6020 chanserv_adjust_limit(void *data
)
6022 struct mod_chanmode change
;
6023 struct chanData
*cData
= data
;
6024 struct chanNode
*channel
= cData
->channel
;
6027 if(IsSuspended(cData
))
6030 cData
->limitAdjusted
= now
;
6031 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6032 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6034 if(limit
> cData
->modes
.new_limit
)
6035 limit
= cData
->modes
.new_limit
;
6036 else if(limit
== cData
->modes
.new_limit
)
6040 mod_chanmode_init(&change
);
6041 change
.modes_set
= MODE_LIMIT
;
6042 change
.new_limit
= limit
;
6043 mod_chanmode_announce(chanserv
, channel
, &change
);
6047 handle_new_channel(struct chanNode
*channel
)
6049 struct chanData
*cData
;
6051 if(!(cData
= channel
->channel_info
))
6054 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6055 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6057 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6058 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6061 /* Welcome to my worst nightmare. Warning: Read (or modify)
6062 the code below at your own risk. */
6064 handle_join(struct modeNode
*mNode
)
6066 struct mod_chanmode change
;
6067 struct userNode
*user
= mNode
->user
;
6068 struct chanNode
*channel
= mNode
->channel
;
6069 struct chanData
*cData
;
6070 struct userData
*uData
= NULL
;
6071 struct banData
*bData
;
6072 struct handle_info
*handle
;
6073 unsigned int modes
= 0, info
= 0;
6076 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6079 cData
= channel
->channel_info
;
6080 if(channel
->members
.used
> cData
->max
)
6081 cData
->max
= channel
->members
.used
;
6083 /* Check for bans. If they're joining through a ban, one of two
6085 * 1: Join during a netburst, by riding the break. Kick them
6086 * unless they have ops or voice in the channel.
6087 * 2: They're allowed to join through the ban (an invite in
6088 * ircu2.10, or a +e on Hybrid, or something).
6089 * If they're not joining through a ban, and the banlist is not
6090 * full, see if they're on the banlist for the channel. If so,
6093 /* This is really, really stupid. not all banned people are kicked.
6094 * sometimes we like to leave them unkicked.
6095 * I tried to explain this to the srvx developers and
6096 * got insulted.. hence one reason for this fork.
6098 if(user->uplink->burst && !mNode->modes)
6101 for(ii = 0; ii < channel->banlist.used; ii++)
6103 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6105 ** Riding a netburst. Naughty. **
6106 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6113 mod_chanmode_init(&change
);
6115 if(channel
->banlist
.used
< MAXBANS
)
6117 /* Not joining through a ban. */
6118 for(bData
= cData
->bans
;
6119 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6120 bData
= bData
->next
);
6124 char kick_reason
[MAXLEN
];
6125 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6127 bData
->triggered
= now
;
6128 if(bData
!= cData
->bans
)
6130 /* Shuffle the ban to the head of the list. */
6132 bData
->next
->prev
= bData
->prev
;
6134 bData
->prev
->next
= bData
->next
;
6137 bData
->next
= cData
->bans
;
6140 cData
->bans
->prev
= bData
;
6141 cData
->bans
= bData
;
6144 change
.args
[0].mode
= MODE_BAN
;
6145 change
.args
[0].u
.hostmask
= bData
->mask
;
6146 mod_chanmode_announce(chanserv
, channel
, &change
);
6147 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6152 /* ChanServ will not modify the limits in join-flooded channels.
6153 It will also skip DynLimit processing when the user (or srvx)
6154 is bursting in, because there are likely more incoming. */
6155 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6156 && !user
->uplink
->burst
6157 && !channel
->join_flooded
6158 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6160 /* The user count has begun "bumping" into the channel limit,
6161 so set a timer to raise the limit a bit. Any previous
6162 timers are removed so three incoming users within the delay
6163 results in one limit change, not three. */
6165 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6166 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6169 if(channel
->join_flooded
)
6171 /* don't automatically give ops or voice during a join flood */
6173 /* I don't understand why we do this, so im removing it -rubin *
6174 else if(cData->lvlOpts[lvlGiveOps] == 0)
6175 modes |= MODE_CHANOP;
6176 else if(cData->lvlOpts[lvlGiveHalfOps] == 0)
6177 modes |= MODE_HALFOP;
6178 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6179 modes |= MODE_VOICE;
6181 greeting
= cData
->greeting
;
6182 if(user
->handle_info
)
6184 handle
= user
->handle_info
;
6186 if(IsHelper(user
) && !IsHelping(user
))
6189 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6191 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6193 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6199 uData
= GetTrueChannelAccess(cData
, handle
);
6200 if(uData
&& !IsUserSuspended(uData
))
6202 /* Ops and above were handled by the above case. */
6203 if(IsUserAutoOp(uData
))
6205 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6206 modes
|= MODE_CHANOP
;
6207 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6208 modes
|= MODE_HALFOP
;
6209 else if(uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice] */)
6210 modes
|= MODE_VOICE
;
6212 if(uData
->access
>= UL_PRESENT
)
6213 cData
->visited
= now
;
6214 if(cData
->user_greeting
)
6215 greeting
= cData
->user_greeting
;
6217 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6218 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6225 if(!user
->uplink
->burst
)
6229 if(modes
& MODE_CHANOP
) {
6230 modes
&= ~MODE_HALFOP
;
6231 modes
&= ~MODE_VOICE
;
6233 change
.args
[0].mode
= modes
;
6234 change
.args
[0].u
.member
= mNode
;
6235 mod_chanmode_announce(chanserv
, channel
, &change
);
6237 if(greeting
&& !user
->uplink
->burst
)
6238 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6240 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6246 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6248 struct mod_chanmode change
;
6249 struct userData
*channel
;
6250 unsigned int ii
, jj
;
6252 if(!user
->handle_info
)
6255 mod_chanmode_init(&change
);
6257 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6259 struct chanNode
*cn
;
6260 struct modeNode
*mn
;
6261 if(IsUserSuspended(channel
)
6262 || IsSuspended(channel
->channel
)
6263 || !(cn
= channel
->channel
->channel
))
6266 mn
= GetUserMode(cn
, user
);
6269 if(!IsUserSuspended(channel
)
6270 && IsUserAutoInvite(channel
)
6271 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6273 && !user
->uplink
->burst
)
6274 irc_invite(chanserv
, user
, cn
);
6278 if(channel
->access
>= UL_PRESENT
)
6279 channel
->channel
->visited
= now
;
6281 if(IsUserAutoOp(channel
))
6283 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6284 change
.args
[0].mode
= MODE_CHANOP
;
6285 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6286 change
.args
[0].mode
= MODE_HALFOP
;
6287 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6288 change
.args
[0].mode
= MODE_VOICE
;
6290 change
.args
[0].mode
= 0;
6291 change
.args
[0].u
.member
= mn
;
6292 if(change
.args
[0].mode
)
6293 mod_chanmode_announce(chanserv
, cn
, &change
);
6296 channel
->seen
= now
;
6297 channel
->present
= 1;
6300 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6302 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6303 struct banData
*ban
;
6305 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6306 || !channel
->channel_info
6307 || IsSuspended(channel
->channel_info
))
6309 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6310 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6312 if(jj
< channel
->banlist
.used
)
6314 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6316 char kick_reason
[MAXLEN
];
6317 if(!user_matches_glob(user
, ban
->mask
, 1))
6319 change
.args
[0].mode
= MODE_BAN
;
6320 change
.args
[0].u
.hostmask
= ban
->mask
;
6321 mod_chanmode_announce(chanserv
, channel
, &change
);
6322 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6323 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6324 ban
->triggered
= now
;
6329 if(IsSupportHelper(user
))
6331 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6333 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6335 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6343 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6345 struct chanData
*cData
;
6346 struct userData
*uData
;
6348 cData
= mn
->channel
->channel_info
;
6349 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6352 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6354 /* Allow for a bit of padding so that the limit doesn't
6355 track the user count exactly, which could get annoying. */
6356 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6358 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6359 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6363 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6365 scan_user_presence(uData
, mn
->user
);
6369 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6371 unsigned int ii
, jj
;
6372 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6374 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6375 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6377 if(jj
< mn
->user
->channels
.used
)
6380 if(ii
== chanserv_conf
.support_channels
.used
)
6381 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6386 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6388 struct userData
*uData
;
6390 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6391 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6392 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6395 if(protect_user(victim
, kicker
, channel
->channel_info
))
6397 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6398 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6401 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6406 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6408 struct chanData
*cData
;
6410 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6413 cData
= channel
->channel_info
;
6414 if(bad_topic(channel
, user
, channel
->topic
))
6415 { /* User doesnt have privs to set topics. Undo it */
6416 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6417 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6420 /* If there is a topic mask set, and the new topic doesnt match,
6421 * set the topic to mask + new_topic */
6422 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6424 char new_topic
[TOPICLEN
+1];
6425 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6428 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6429 /* and fall through to topicsnarf code below.. */
6431 else /* Topic couldnt fit into mask, was too long */
6433 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6434 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6435 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6439 /* With topicsnarf, grab the topic and save it as the default topic. */
6440 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6443 cData
->topic
= strdup(channel
->topic
);
6449 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6451 struct mod_chanmode
*bounce
= NULL
;
6452 unsigned int bnc
, ii
;
6455 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6458 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6459 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6461 char correct
[MAXLEN
];
6462 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6463 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6464 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6466 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6468 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6470 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6471 if(!protect_user(victim
, user
, channel
->channel_info
))
6474 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6477 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6478 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6479 if(bounce
->args
[bnc
].u
.member
)
6483 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6484 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6486 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6488 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6490 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6491 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6494 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6495 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6496 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6499 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6501 const char *ban
= change
->args
[ii
].u
.hostmask
;
6502 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6505 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6506 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6507 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6509 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6514 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6515 mod_chanmode_announce(chanserv
, channel
, bounce
);
6516 for(ii
= 0; ii
< change
->argc
; ++ii
)
6517 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6518 free((char*)bounce
->args
[ii
].u
.hostmask
);
6519 mod_chanmode_free(bounce
);
6524 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6526 struct chanNode
*channel
;
6527 struct banData
*bData
;
6528 struct mod_chanmode change
;
6529 unsigned int ii
, jj
;
6530 char kick_reason
[MAXLEN
];
6532 mod_chanmode_init(&change
);
6534 change
.args
[0].mode
= MODE_BAN
;
6535 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6537 channel
= user
->channels
.list
[ii
]->channel
;
6538 /* Need not check for bans if they're opped or voiced. */
6539 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6541 /* Need not check for bans unless channel registration is active. */
6542 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6544 /* Look for a matching ban already on the channel. */
6545 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6546 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6548 /* Need not act if we found one. */
6549 if(jj
< channel
->banlist
.used
)
6551 /* Look for a matching ban in this channel. */
6552 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6554 if(!user_matches_glob(user
, bData
->mask
, 1))
6556 change
.args
[0].u
.hostmask
= bData
->mask
;
6557 mod_chanmode_announce(chanserv
, channel
, &change
);
6558 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6559 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6560 bData
->triggered
= now
;
6561 break; /* we don't need to check any more bans in the channel */
6566 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6568 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6572 dict_remove2(handle_dnrs
, old_handle
, 1);
6573 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6574 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6579 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6581 struct userNode
*h_user
;
6583 if(handle
->channels
)
6585 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6586 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6588 while(handle
->channels
)
6589 del_channel_user(handle
->channels
, 1);
6594 handle_server_link(UNUSED_ARG(struct server
*server
))
6596 struct chanData
*cData
;
6598 for(cData
= channelList
; cData
; cData
= cData
->next
)
6600 if(!IsSuspended(cData
))
6601 cData
->may_opchan
= 1;
6602 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6603 && !cData
->channel
->join_flooded
6604 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6605 < chanserv_conf
.adjust_threshold
))
6607 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6608 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6614 chanserv_conf_read(void)
6618 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6619 struct mod_chanmode
*change
;
6620 struct string_list
*strlist
;
6621 struct chanNode
*chan
;
6624 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6626 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6629 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6630 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6631 chanserv_conf
.support_channels
.used
= 0;
6632 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6634 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6636 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6639 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6641 channelList_append(&chanserv_conf
.support_channels
, chan
);
6644 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6647 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6650 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6652 channelList_append(&chanserv_conf
.support_channels
, chan
);
6654 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6655 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6656 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6657 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6658 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6659 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6660 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6661 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6662 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6663 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6664 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6665 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6666 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6667 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6668 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6669 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6670 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6671 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6672 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6673 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6674 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6675 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6676 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6678 NickChange(chanserv
, str
, 0);
6679 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6680 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6681 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6682 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6683 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6684 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6685 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6686 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6687 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6688 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6689 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6690 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6691 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6692 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6693 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6694 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6695 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6698 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6699 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6700 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6702 chanserv_conf
.default_modes
= *change
;
6703 mod_chanmode_free(change
);
6705 free_string_list(chanserv_conf
.set_shows
);
6706 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6708 strlist
= string_list_copy(strlist
);
6711 static const char *list
[] = {
6712 /* free form text */
6713 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6714 /* options based on user level */
6715 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
6716 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6717 /* multiple choice options */
6718 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6719 /* binary options */
6720 "DynLimit", "NoDelete",
6725 strlist
= alloc_string_list(ArrayLength(list
)-1);
6726 for(ii
=0; list
[ii
]; ii
++)
6727 string_list_append(strlist
, strdup(list
[ii
]));
6729 chanserv_conf
.set_shows
= strlist
;
6730 /* We don't look things up now, in case the list refers to options
6731 * defined by modules initialized after this point. Just mark the
6732 * function list as invalid, so it will be initialized.
6734 set_shows_list
.used
= 0;
6735 free_string_list(chanserv_conf
.eightball
);
6736 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6739 strlist
= string_list_copy(strlist
);
6743 strlist
= alloc_string_list(4);
6744 string_list_append(strlist
, strdup("Yes."));
6745 string_list_append(strlist
, strdup("No."));
6746 string_list_append(strlist
, strdup("Maybe so."));
6748 chanserv_conf
.eightball
= strlist
;
6749 free_string_list(chanserv_conf
.old_ban_names
);
6750 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6752 strlist
= string_list_copy(strlist
);
6754 strlist
= alloc_string_list(2);
6755 chanserv_conf
.old_ban_names
= strlist
;
6756 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6757 off_channel
= str
? atoi(str
) : 0;
6761 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6764 struct note_type
*ntype
;
6767 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6769 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6772 if(!(ntype
= chanserv_create_note_type(key
)))
6774 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6778 /* Figure out set access */
6779 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6781 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6782 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6784 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6786 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6787 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6789 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6791 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6795 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6796 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6797 ntype
->set_access
.min_opserv
= 0;
6800 /* Figure out visibility */
6801 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6802 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6803 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6804 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6805 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6806 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6807 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6808 ntype
->visible_type
= NOTE_VIS_ALL
;
6810 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6812 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6813 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6817 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6819 struct handle_info
*handle
;
6820 struct userData
*uData
;
6821 char *seen
, *inf
, *flags
;
6823 unsigned short access
;
6825 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6827 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6831 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6832 if(access
> UL_OWNER
)
6834 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6838 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
6839 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
6840 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
6841 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
6842 handle
= get_handle_info(key
);
6845 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
6849 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
6850 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
6854 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6856 struct banData
*bData
;
6857 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
6858 time_t set_time
, triggered_time
, expires_time
;
6860 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6862 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
6866 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
6867 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
6868 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
6869 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
6870 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
6871 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
6872 if (!reason
|| !owner
)
6875 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
6876 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
6878 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
6880 expires_time
= set_time
+ atoi(s_duration
);
6884 if(!reason
|| (expires_time
&& (expires_time
< now
)))
6887 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
6890 static struct suspended
*
6891 chanserv_read_suspended(dict_t obj
)
6893 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
6897 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
6898 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6899 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
6900 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6901 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
6902 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6903 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
6904 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
6905 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
6906 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
6911 chanserv_channel_read(const char *key
, struct record_data
*hir
)
6913 struct suspended
*suspended
;
6914 struct mod_chanmode
*modes
;
6915 struct chanNode
*cNode
;
6916 struct chanData
*cData
;
6917 struct dict
*channel
, *obj
;
6918 char *str
, *argv
[10];
6922 channel
= hir
->d
.object
;
6924 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
6927 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
6930 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
6933 cData
= register_channel(cNode
, str
);
6936 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
6940 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
6942 enum levelOption lvlOpt
;
6943 enum charOption chOpt
;
6945 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
6946 cData
->flags
= atoi(str
);
6948 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6950 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
6952 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
6953 else if(levelOptions
[lvlOpt
].old_flag
)
6955 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6956 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
6958 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6962 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6964 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
6966 cData
->chOpts
[chOpt
] = str
[0];
6969 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
6971 enum levelOption lvlOpt
;
6972 enum charOption chOpt
;
6975 cData
->flags
= base64toint(str
, 5);
6976 count
= strlen(str
+= 5);
6977 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6980 if(levelOptions
[lvlOpt
].old_flag
)
6982 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6983 lvl
= levelOptions
[lvlOpt
].flag_value
;
6985 lvl
= levelOptions
[lvlOpt
].default_value
;
6987 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
6989 case 'c': lvl
= UL_COOWNER
; break;
6990 case 'm': lvl
= UL_MANAGER
; break;
6991 case 'n': lvl
= UL_OWNER
+1; break;
6992 case 'o': lvl
= UL_OP
; break;
6993 case 'p': lvl
= UL_PEON
; break;
6994 case 'h': lvl
= UL_HALFOP
; break;
6995 case 'w': lvl
= UL_OWNER
; break;
6996 default: lvl
= 0; break;
6998 cData
->lvlOpts
[lvlOpt
] = lvl
;
7000 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7001 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7004 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7006 suspended
= chanserv_read_suspended(obj
);
7007 cData
->suspended
= suspended
;
7008 suspended
->cData
= cData
;
7009 /* We could use suspended->expires and suspended->revoked to
7010 * set the CHANNEL_SUSPENDED flag, but we don't. */
7012 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7014 suspended
= calloc(1, sizeof(*suspended
));
7015 suspended
->issued
= 0;
7016 suspended
->revoked
= 0;
7017 suspended
->suspender
= strdup(str
);
7018 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7019 suspended
->expires
= str
? atoi(str
) : 0;
7020 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7021 suspended
->reason
= strdup(str
? str
: "No reason");
7022 suspended
->previous
= NULL
;
7023 cData
->suspended
= suspended
;
7024 suspended
->cData
= cData
;
7028 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7029 suspended
= NULL
; /* to squelch a warning */
7032 if(IsSuspended(cData
)) {
7033 if(suspended
->expires
> now
)
7034 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7035 else if(suspended
->expires
)
7036 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7039 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7040 struct mod_chanmode change
;
7041 mod_chanmode_init(&change
);
7043 change
.args
[0].mode
= MODE_CHANOP
;
7044 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7045 mod_chanmode_announce(chanserv
, cNode
, &change
);
7048 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7049 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7050 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7051 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7052 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7053 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7054 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7055 cData
->max
= str
? atoi(str
) : 0;
7056 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7057 cData
->greeting
= str
? strdup(str
) : NULL
;
7058 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7059 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7060 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7061 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7062 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7063 cData
->topic
= str
? strdup(str
) : NULL
;
7065 if(!IsSuspended(cData
)
7066 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7067 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7068 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7069 cData
->modes
= *modes
;
7071 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7072 if(cData
->modes
.argc
> 1)
7073 cData
->modes
.argc
= 1;
7074 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7075 mod_chanmode_free(modes
);
7078 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7079 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7080 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7082 if(!cData
->users
&& !IsProtected(cData
))
7084 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7085 unregister_channel(cData
, "has empty user list.");
7089 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7090 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7091 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7093 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7094 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7096 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7097 struct record_data
*rd
= iter_data(it
);
7098 const char *note
, *setter
;
7100 if(rd
->type
!= RECDB_OBJECT
)
7102 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7106 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7108 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7110 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7114 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7115 if(!setter
) setter
= "<unknown>";
7116 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7124 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7126 const char *setter
, *reason
, *str
;
7127 struct do_not_register
*dnr
;
7129 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7132 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7135 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7138 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7141 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7144 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7146 dnr
->set
= atoi(str
);
7152 chanserv_saxdb_read(struct dict
*database
)
7154 struct dict
*section
;
7157 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7158 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7159 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7161 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7162 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7163 chanserv_channel_read(iter_key(it
), iter_data(it
));
7165 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7166 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7167 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7173 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7175 int high_present
= 0;
7176 saxdb_start_record(ctx
, KEY_USERS
, 1);
7177 for(; uData
; uData
= uData
->next
)
7179 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7181 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7182 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7183 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7185 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7187 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7188 saxdb_end_record(ctx
);
7190 saxdb_end_record(ctx
);
7191 return high_present
;
7195 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7199 saxdb_start_record(ctx
, KEY_BANS
, 1);
7200 for(; bData
; bData
= bData
->next
)
7202 saxdb_start_record(ctx
, bData
->mask
, 0);
7203 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7204 if(bData
->triggered
)
7205 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7207 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7209 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7211 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7212 saxdb_end_record(ctx
);
7214 saxdb_end_record(ctx
);
7218 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7220 saxdb_start_record(ctx
, name
, 0);
7221 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7222 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7224 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7226 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7228 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7230 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7231 saxdb_end_record(ctx
);
7235 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7239 enum levelOption lvlOpt
;
7240 enum charOption chOpt
;
7242 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7244 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7245 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7247 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7248 if(channel
->registrar
)
7249 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7250 if(channel
->greeting
)
7251 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7252 if(channel
->user_greeting
)
7253 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7254 if(channel
->topic_mask
)
7255 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7256 if(channel
->suspended
)
7257 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7259 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7260 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7261 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7262 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7263 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7265 buf
[0] = channel
->chOpts
[chOpt
];
7267 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7269 saxdb_end_record(ctx
);
7271 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7273 mod_chanmode_format(&channel
->modes
, buf
);
7274 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7277 high_present
= chanserv_write_users(ctx
, channel
->users
);
7278 chanserv_write_bans(ctx
, channel
->bans
);
7280 if(dict_size(channel
->notes
))
7284 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7285 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7287 struct note
*note
= iter_data(it
);
7288 saxdb_start_record(ctx
, iter_key(it
), 0);
7289 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7290 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7291 saxdb_end_record(ctx
);
7293 saxdb_end_record(ctx
);
7296 if(channel
->ownerTransfer
)
7297 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7298 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7299 saxdb_end_record(ctx
);
7303 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7307 saxdb_start_record(ctx
, ntype
->name
, 0);
7308 switch(ntype
->set_access_type
)
7310 case NOTE_SET_CHANNEL_ACCESS
:
7311 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7313 case NOTE_SET_CHANNEL_SETTER
:
7314 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7316 case NOTE_SET_PRIVILEGED
: default:
7317 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7320 switch(ntype
->visible_type
)
7322 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7323 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7324 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7326 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7327 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7328 saxdb_end_record(ctx
);
7332 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7334 struct do_not_register
*dnr
;
7337 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7339 dnr
= iter_data(it
);
7340 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7342 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7343 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7344 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7345 saxdb_end_record(ctx
);
7350 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7353 struct chanData
*channel
;
7356 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7357 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7358 chanserv_write_note_type(ctx
, iter_data(it
));
7359 saxdb_end_record(ctx
);
7362 saxdb_start_record(ctx
, KEY_DNR
, 1);
7363 write_dnrs_helper(ctx
, handle_dnrs
);
7364 write_dnrs_helper(ctx
, plain_dnrs
);
7365 write_dnrs_helper(ctx
, mask_dnrs
);
7366 saxdb_end_record(ctx
);
7369 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7370 for(channel
= channelList
; channel
; channel
= channel
->next
)
7371 chanserv_write_channel(ctx
, channel
);
7372 saxdb_end_record(ctx
);
7378 chanserv_db_cleanup(void) {
7380 unreg_part_func(handle_part
);
7382 unregister_channel(channelList
, "terminating.");
7383 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7384 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7385 free(chanserv_conf
.support_channels
.list
);
7386 dict_delete(handle_dnrs
);
7387 dict_delete(plain_dnrs
);
7388 dict_delete(mask_dnrs
);
7389 dict_delete(note_types
);
7390 free_string_list(chanserv_conf
.eightball
);
7391 free_string_list(chanserv_conf
.old_ban_names
);
7392 free_string_list(chanserv_conf
.set_shows
);
7393 free(set_shows_list
.list
);
7394 free(uset_shows_list
.list
);
7397 struct userData
*helper
= helperList
;
7398 helperList
= helperList
->next
;
7403 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7404 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7405 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7408 init_chanserv(const char *nick
)
7410 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7411 conf_register_reload(chanserv_conf_read
);
7413 reg_server_link_func(handle_server_link
);
7415 reg_new_channel_func(handle_new_channel
);
7416 reg_join_func(handle_join
);
7417 reg_part_func(handle_part
);
7418 reg_kick_func(handle_kick
);
7419 reg_topic_func(handle_topic
);
7420 reg_mode_change_func(handle_mode
);
7421 reg_nick_change_func(handle_nick_change
);
7423 reg_auth_func(handle_auth
);
7424 reg_handle_rename_func(handle_rename
);
7425 reg_unreg_func(handle_unreg
);
7427 handle_dnrs
= dict_new();
7428 dict_set_free_data(handle_dnrs
, free
);
7429 plain_dnrs
= dict_new();
7430 dict_set_free_data(plain_dnrs
, free
);
7431 mask_dnrs
= dict_new();
7432 dict_set_free_data(mask_dnrs
, free
);
7434 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7435 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7436 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7437 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7438 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7439 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7440 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7441 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7442 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7443 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7445 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7446 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7448 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7449 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7450 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7451 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7452 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7454 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7455 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7456 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7457 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7458 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7459 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7461 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7462 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7463 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7464 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7466 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7467 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7468 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7469 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7470 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7471 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7472 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7473 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7474 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7475 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7477 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7478 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7479 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7480 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7481 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7482 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7483 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7484 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7485 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7486 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7487 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7488 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7489 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7490 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7492 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7493 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7494 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7495 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7496 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7498 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7499 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7501 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7502 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7503 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7504 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7505 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7506 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7507 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7508 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7509 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7510 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7511 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7512 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7514 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7515 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7517 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7518 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7519 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7520 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7522 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7523 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7524 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7525 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7526 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7528 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7529 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7530 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7531 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7532 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7533 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7534 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7536 /* Channel options */
7537 DEFINE_CHANNEL_OPTION(defaulttopic
);
7538 DEFINE_CHANNEL_OPTION(topicmask
);
7539 DEFINE_CHANNEL_OPTION(greeting
);
7540 DEFINE_CHANNEL_OPTION(usergreeting
);
7541 DEFINE_CHANNEL_OPTION(modes
);
7542 DEFINE_CHANNEL_OPTION(enfops
);
7543 DEFINE_CHANNEL_OPTION(enfhalfops
);
7544 /*DEFINE_CHANNEL_OPTION(giveops);
7545 DEFINE_CHANNEL_OPTION(givehalfops);
7547 DEFINE_CHANNEL_OPTION(protect
);
7548 DEFINE_CHANNEL_OPTION(enfmodes
);
7549 DEFINE_CHANNEL_OPTION(enftopic
);
7550 DEFINE_CHANNEL_OPTION(pubcmd
);
7551 /*DEFINE_CHANNEL_OPTION(givevoice);
7553 DEFINE_CHANNEL_OPTION(userinfo
);
7554 DEFINE_CHANNEL_OPTION(dynlimit
);
7555 DEFINE_CHANNEL_OPTION(topicsnarf
);
7556 DEFINE_CHANNEL_OPTION(nodelete
);
7557 DEFINE_CHANNEL_OPTION(toys
);
7558 DEFINE_CHANNEL_OPTION(setters
);
7559 DEFINE_CHANNEL_OPTION(topicrefresh
);
7560 DEFINE_CHANNEL_OPTION(ctcpusers
);
7561 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7562 DEFINE_CHANNEL_OPTION(inviteme
);
7564 DEFINE_CHANNEL_OPTION(offchannel
);
7565 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7567 /* Alias set topic to set defaulttopic for compatibility. */
7568 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7571 DEFINE_USER_OPTION(noautoop
);
7572 DEFINE_USER_OPTION(autoinvite
);
7573 DEFINE_USER_OPTION(info
);
7575 /* Alias uset autovoice to uset autoop. */
7576 modcmd_register(chanserv_module
, "uset noautovoice", user_opt_noautoop
, 1, 0, NULL
);
7578 note_types
= dict_new();
7579 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7582 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7583 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7584 service_register(chanserv
)->trigger
= '!';
7585 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7587 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7589 if(chanserv_conf
.channel_expire_frequency
)
7590 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7592 if(chanserv_conf
.refresh_period
)
7594 time_t next_refresh
;
7595 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7596 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7599 reg_exit_func(chanserv_db_cleanup
);
7600 message_register_table(msgtab
);