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", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
176 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
177 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
178 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
179 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
181 /* Removing yourself from a channel. */
182 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
183 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
184 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
186 /* User management */
187 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
188 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
189 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
190 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
191 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
192 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
193 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
194 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
196 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
197 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
198 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
199 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
200 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
201 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
204 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
205 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
206 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
207 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
208 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
209 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
210 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
211 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
212 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
213 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
214 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
215 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
216 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
217 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
218 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
219 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
221 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
223 /* Channel management */
224 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
225 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
226 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
228 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
229 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
230 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
231 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
232 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
233 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
234 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
236 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
237 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
238 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
239 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
240 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
241 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
242 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
243 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
244 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
245 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
246 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
247 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
248 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
249 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
250 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
251 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
252 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
253 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
254 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
255 { "CSMSG_SET_MODES", "$bModes $b %s" },
256 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
257 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
258 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
259 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
260 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
261 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
262 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
263 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
264 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
265 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
266 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
267 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
268 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
269 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
270 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
271 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
272 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
273 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
274 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
275 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
276 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
277 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
278 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
279 { "CSMSG_USET_INFO", "$bInfo $b %s" },
281 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
282 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
283 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
284 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
285 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
286 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
287 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
288 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
289 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
290 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
291 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
292 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
293 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
294 { "CSMSG_PROTECT_NONE", "No users will be protected." },
295 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
296 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
297 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
298 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
299 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
300 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
301 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
302 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
303 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
304 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
305 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
308 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
309 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
310 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
311 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
312 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
313 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
314 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
315 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
317 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
318 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
319 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
321 /* Channel userlist */
322 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
323 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
324 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
325 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
327 /* Channel note list */
328 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
329 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
330 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
331 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
332 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
333 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
334 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
335 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
336 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
337 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
338 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
339 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
340 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
341 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
342 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
344 /* Channel [un]suspension */
345 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
346 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
347 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
348 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
349 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
350 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
351 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
353 /* Access information */
354 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
355 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
356 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
357 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
358 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
359 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
360 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
361 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
362 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
363 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
364 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
366 /* Seen information */
367 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
368 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
369 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
370 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
372 /* Names information */
373 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
374 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
376 /* Channel information */
377 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
378 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
379 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
380 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
381 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
382 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
383 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
384 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
385 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
386 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
387 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
388 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
390 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
396 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
397 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
399 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
400 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
401 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
402 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
403 { "CSMSG_PEEK_OPS", "$bOps:$b" },
404 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
406 /* Network information */
407 { "CSMSG_NETWORK_INFO", "Network Information:" },
408 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
409 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
410 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
411 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
412 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
413 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
414 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
415 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
418 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
419 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
420 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
422 /* Channel searches */
423 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
424 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
425 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
426 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
428 /* Channel configuration */
429 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
430 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
431 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
434 { "CSMSG_USER_OPTIONS", "User Options:" },
435 { "CSMSG_USER_PROTECTED", "That user is protected." },
438 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
439 { "CSMSG_PING_RESPONSE", "Pong!" },
440 { "CSMSG_WUT_RESPONSE", "wut" },
441 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
442 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
443 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
444 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
445 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
446 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
447 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
450 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
454 /* eject_user and unban_user flags */
455 #define ACTION_KICK 0x0001
456 #define ACTION_BAN 0x0002
457 #define ACTION_ADD_BAN 0x0004
458 #define ACTION_ADD_TIMED_BAN 0x0008
459 #define ACTION_UNBAN 0x0010
460 #define ACTION_DEL_BAN 0x0020
462 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
463 #define MODELEN 40 + KEYLEN
467 #define CSFUNC_ARGS user, channel, argc, argv, cmd
469 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
470 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
471 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
472 reply("MSG_MISSING_PARAMS", argv[0]); \
476 DECLARE_LIST(dnrList
, struct do_not_register
*);
477 DEFINE_LIST(dnrList
, struct do_not_register
*);
479 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
481 struct userNode
*chanserv
;
484 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
485 static struct log_type
*CS_LOG
;
489 struct channelList support_channels
;
490 struct mod_chanmode default_modes
;
492 unsigned long db_backup_frequency
;
493 unsigned long channel_expire_frequency
;
496 unsigned int adjust_delay
;
497 long channel_expire_delay
;
498 unsigned int nodelete_level
;
500 unsigned int adjust_threshold
;
501 int join_flood_threshold
;
503 unsigned int greeting_length
;
504 unsigned int refresh_period
;
505 unsigned int giveownership_period
;
507 unsigned int max_owned
;
508 unsigned int max_chan_users
;
509 unsigned int max_chan_bans
;
510 unsigned int max_userinfo_length
;
512 struct string_list
*set_shows
;
513 struct string_list
*eightball
;
514 struct string_list
*old_ban_names
;
516 const char *ctcp_short_ban_duration
;
517 const char *ctcp_long_ban_duration
;
519 const char *irc_operator_epithet
;
520 const char *network_helper_epithet
;
521 const char *support_helper_epithet
;
526 struct userNode
*user
;
527 struct userNode
*bot
;
528 struct chanNode
*channel
;
530 unsigned short lowest
;
531 unsigned short highest
;
532 struct userData
**users
;
533 struct helpfile_table table
;
536 enum note_access_type
538 NOTE_SET_CHANNEL_ACCESS
,
539 NOTE_SET_CHANNEL_SETTER
,
543 enum note_visible_type
546 NOTE_VIS_CHANNEL_USERS
,
552 enum note_access_type set_access_type
;
554 unsigned int min_opserv
;
555 unsigned short min_ulevel
;
557 enum note_visible_type visible_type
;
558 unsigned int max_length
;
565 struct note_type
*type
;
566 char setter
[NICKSERV_HANDLE_LEN
+1];
570 static unsigned int registered_channels
;
571 static unsigned int banCount
;
573 static const struct {
576 unsigned short level
;
579 { "peon", "Peon", UL_PEON
, '+' },
580 { "halfop", "HalfOp", UL_HALFOP
, '%' },
581 { "op", "Op", UL_OP
, '@' },
582 { "manager", "Manager", UL_MANAGER
, '%' },
583 { "coowner", "Coowner", UL_COOWNER
, '*' },
584 { "owner", "Owner", UL_OWNER
, '!' },
585 { "helper", "BUG:", UL_HELPER
, 'X' }
588 static const struct {
591 unsigned short default_value
;
592 unsigned int old_idx
;
593 unsigned int old_flag
;
594 unsigned short flag_value
;
596 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
597 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
598 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
599 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
600 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
601 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
602 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
603 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
604 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
605 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
606 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
607 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
608 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
611 struct charOptionValues
{
614 } protectValues
[] = {
615 { 'a', "CSMSG_PROTECT_ALL" },
616 { 'e', "CSMSG_PROTECT_EQUAL" },
617 { 'l', "CSMSG_PROTECT_LOWER" },
618 { 'n', "CSMSG_PROTECT_NONE" }
620 { 'd', "CSMSG_TOYS_DISABLED" },
621 { 'n', "CSMSG_TOYS_PRIVATE" },
622 { 'p', "CSMSG_TOYS_PUBLIC" }
623 }, topicRefreshValues
[] = {
624 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
625 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
626 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
627 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
628 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
629 }, ctcpReactionValues
[] = {
630 { 'k', "CSMSG_CTCPREACTION_KICK" },
631 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
632 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
633 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
636 static const struct {
640 unsigned int old_idx
;
642 struct charOptionValues
*values
;
644 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
645 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
646 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
647 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
650 struct userData
*helperList
;
651 struct chanData
*channelList
;
652 static struct module *chanserv_module
;
653 static unsigned int userCount
;
655 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
656 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
657 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
660 user_level_from_name(const char *name
, unsigned short clamp_level
)
662 unsigned int level
= 0, ii
;
664 level
= strtoul(name
, NULL
, 10);
665 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
666 if(!irccasecmp(name
, accessLevels
[ii
].name
))
667 level
= accessLevels
[ii
].level
;
668 if(level
> clamp_level
)
674 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
677 *minl
= strtoul(arg
, &sep
, 10);
685 *maxl
= strtoul(sep
+1, &sep
, 10);
693 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
695 struct userData
*uData
, **head
;
697 if(!channel
|| !handle
)
700 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
701 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
703 for(uData
= helperList
;
704 uData
&& uData
->handle
!= handle
;
705 uData
= uData
->next
);
709 uData
= calloc(1, sizeof(struct userData
));
710 uData
->handle
= handle
;
712 uData
->access
= UL_HELPER
;
718 uData
->next
= helperList
;
720 helperList
->prev
= uData
;
728 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
729 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
732 head
= &(channel
->users
);
735 if(uData
&& (uData
!= *head
))
737 /* Shuffle the user to the head of whatever list he was in. */
739 uData
->next
->prev
= uData
->prev
;
741 uData
->prev
->next
= uData
->next
;
747 (**head
).prev
= uData
;
754 /* Returns non-zero if user has at least the minimum access.
755 * exempt_owner is set when handling !set, so the owner can set things
758 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
760 struct userData
*uData
;
761 struct chanData
*cData
= channel
->channel_info
;
762 unsigned short minimum
= cData
->lvlOpts
[opt
];
765 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
768 if(minimum
<= uData
->access
)
770 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
775 /* Scan for other users authenticated to the same handle
776 still in the channel. If so, keep them listed as present.
778 user is optional, if not null, it skips checking that userNode
779 (for the handle_part function) */
781 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
785 if(IsSuspended(uData
->channel
)
786 || IsUserSuspended(uData
)
787 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
799 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
801 unsigned int eflags
, argc
;
803 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
805 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
806 if(!channel
->channel_info
807 || IsSuspended(channel
->channel_info
)
809 || !ircncasecmp(text
, "ACTION ", 7))
811 /* Figure out the minimum level needed to CTCP the channel */
812 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
814 /* We need to enforce against them; do so. */
817 argv
[1] = user
->nick
;
819 if(GetUserMode(channel
, user
))
820 eflags
|= ACTION_KICK
;
821 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
822 default: case 'k': /* just do the kick */ break;
824 eflags
|= ACTION_BAN
;
827 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
828 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
831 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
832 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
835 argv
[argc
++] = bad_ctcp_reason
;
836 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
840 chanserv_create_note_type(const char *name
)
842 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
843 strcpy(ntype
->name
, name
);
845 dict_insert(note_types
, ntype
->name
, ntype
);
850 chanserv_deref_note_type(void *data
)
852 struct note_type
*ntype
= data
;
854 if(--ntype
->refs
> 0)
860 chanserv_flush_note_type(struct note_type
*ntype
)
862 struct chanData
*cData
;
863 for(cData
= channelList
; cData
; cData
= cData
->next
)
864 dict_remove(cData
->notes
, ntype
->name
);
868 chanserv_truncate_notes(struct note_type
*ntype
)
870 struct chanData
*cData
;
872 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
874 for(cData
= channelList
; cData
; cData
= cData
->next
) {
875 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
878 if(strlen(note
->note
) <= ntype
->max_length
)
880 dict_remove2(cData
->notes
, ntype
->name
, 1);
881 note
= realloc(note
, size
);
882 note
->note
[ntype
->max_length
] = 0;
883 dict_insert(cData
->notes
, ntype
->name
, note
);
887 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
890 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
893 unsigned int len
= strlen(text
);
895 if(len
> type
->max_length
) len
= type
->max_length
;
896 note
= calloc(1, sizeof(*note
) + len
);
898 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
899 memcpy(note
->note
, text
, len
);
901 dict_insert(channel
->notes
, type
->name
, note
);
907 chanserv_free_note(void *data
)
909 struct note
*note
= data
;
911 chanserv_deref_note_type(note
->type
);
912 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
916 static MODCMD_FUNC(cmd_createnote
) {
917 struct note_type
*ntype
;
918 unsigned int arg
= 1, existed
= 0, max_length
;
920 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
923 ntype
= chanserv_create_note_type(argv
[arg
]);
924 if(!irccasecmp(argv
[++arg
], "privileged"))
927 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
928 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
930 else if(!irccasecmp(argv
[arg
], "channel"))
932 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
935 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
938 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
939 ntype
->set_access
.min_ulevel
= ulvl
;
941 else if(!irccasecmp(argv
[arg
], "setter"))
943 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
947 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
951 if(!irccasecmp(argv
[++arg
], "privileged"))
952 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
953 else if(!irccasecmp(argv
[arg
], "channel_users"))
954 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
955 else if(!irccasecmp(argv
[arg
], "all"))
956 ntype
->visible_type
= NOTE_VIS_ALL
;
958 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
962 if((arg
+1) >= argc
) {
963 reply("MSG_MISSING_PARAMS", argv
[0]);
966 max_length
= strtoul(argv
[++arg
], NULL
, 0);
967 if(max_length
< 20 || max_length
> 450)
969 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
972 if(existed
&& (max_length
< ntype
->max_length
))
974 ntype
->max_length
= max_length
;
975 chanserv_truncate_notes(ntype
);
977 ntype
->max_length
= max_length
;
980 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
982 reply("CSMSG_NOTE_CREATED", ntype
->name
);
987 dict_remove(note_types
, ntype
->name
);
991 static MODCMD_FUNC(cmd_removenote
) {
992 struct note_type
*ntype
;
995 ntype
= dict_find(note_types
, argv
[1], NULL
);
996 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
999 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1006 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1009 chanserv_flush_note_type(ntype
);
1011 dict_remove(note_types
, argv
[1]);
1012 reply("CSMSG_NOTE_DELETED", argv
[1]);
1017 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1021 if(orig
->modes_set
& change
->modes_clear
)
1023 if(orig
->modes_clear
& change
->modes_set
)
1025 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1026 && strcmp(orig
->new_key
, change
->new_key
))
1028 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1029 && (orig
->new_limit
!= change
->new_limit
))
1034 static char max_length_text
[MAXLEN
+1][16];
1036 static struct helpfile_expansion
1037 chanserv_expand_variable(const char *variable
)
1039 struct helpfile_expansion exp
;
1041 if(!irccasecmp(variable
, "notes"))
1044 exp
.type
= HF_TABLE
;
1045 exp
.value
.table
.length
= 1;
1046 exp
.value
.table
.width
= 3;
1047 exp
.value
.table
.flags
= 0;
1048 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1049 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1050 exp
.value
.table
.contents
[0][0] = "Note Type";
1051 exp
.value
.table
.contents
[0][1] = "Visibility";
1052 exp
.value
.table
.contents
[0][2] = "Max Length";
1053 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1055 struct note_type
*ntype
= iter_data(it
);
1058 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1059 row
= exp
.value
.table
.length
++;
1060 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1061 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1062 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1063 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1065 if(!max_length_text
[ntype
->max_length
][0])
1066 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1067 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1072 exp
.type
= HF_STRING
;
1073 exp
.value
.str
= NULL
;
1077 static struct chanData
*
1078 register_channel(struct chanNode
*cNode
, char *registrar
)
1080 struct chanData
*channel
;
1081 enum levelOption lvlOpt
;
1082 enum charOption chOpt
;
1084 channel
= calloc(1, sizeof(struct chanData
));
1086 channel
->notes
= dict_new();
1087 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1089 channel
->registrar
= strdup(registrar
);
1090 channel
->registered
= now
;
1091 channel
->visited
= now
;
1092 channel
->limitAdjusted
= now
;
1093 channel
->ownerTransfer
= now
;
1094 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1095 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1096 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1097 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1098 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1100 channel
->prev
= NULL
;
1101 channel
->next
= channelList
;
1104 channelList
->prev
= channel
;
1105 channelList
= channel
;
1106 registered_channels
++;
1108 channel
->channel
= cNode
;
1110 cNode
->channel_info
= channel
;
1115 static struct userData
*
1116 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1118 struct userData
*ud
;
1120 if(access
> UL_OWNER
)
1123 ud
= calloc(1, sizeof(*ud
));
1124 ud
->channel
= channel
;
1125 ud
->handle
= handle
;
1127 ud
->access
= access
;
1128 ud
->info
= info
? strdup(info
) : NULL
;
1131 ud
->next
= channel
->users
;
1133 channel
->users
->prev
= ud
;
1134 channel
->users
= ud
;
1136 channel
->userCount
++;
1140 ud
->u_next
= ud
->handle
->channels
;
1142 ud
->u_next
->u_prev
= ud
;
1143 ud
->handle
->channels
= ud
;
1148 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1151 del_channel_user(struct userData
*user
, int do_gc
)
1153 struct chanData
*channel
= user
->channel
;
1155 channel
->userCount
--;
1159 user
->prev
->next
= user
->next
;
1161 channel
->users
= user
->next
;
1163 user
->next
->prev
= user
->prev
;
1166 user
->u_prev
->u_next
= user
->u_next
;
1168 user
->handle
->channels
= user
->u_next
;
1170 user
->u_next
->u_prev
= user
->u_prev
;
1174 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1175 unregister_channel(channel
, "lost all users.");
1178 static void expire_ban(void *data
);
1180 static struct banData
*
1181 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1184 unsigned int ii
, l1
, l2
;
1189 bd
= malloc(sizeof(struct banData
));
1191 bd
->channel
= channel
;
1193 bd
->triggered
= triggered
;
1194 bd
->expires
= expires
;
1196 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1198 extern const char *hidden_host_suffix
;
1199 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1203 l2
= strlen(old_name
);
1206 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1208 new_mask
= alloca(MAXLEN
);
1209 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1212 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1214 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1215 bd
->reason
= strdup(reason
);
1218 timeq_add(expires
, expire_ban
, bd
);
1221 bd
->next
= channel
->bans
;
1223 channel
->bans
->prev
= bd
;
1225 channel
->banCount
++;
1232 del_channel_ban(struct banData
*ban
)
1234 ban
->channel
->banCount
--;
1238 ban
->prev
->next
= ban
->next
;
1240 ban
->channel
->bans
= ban
->next
;
1243 ban
->next
->prev
= ban
->prev
;
1246 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1255 expire_ban(void *data
)
1257 struct banData
*bd
= data
;
1258 if(!IsSuspended(bd
->channel
))
1260 struct banList bans
;
1261 struct mod_chanmode change
;
1263 bans
= bd
->channel
->channel
->banlist
;
1264 mod_chanmode_init(&change
);
1265 for(ii
=0; ii
<bans
.used
; ii
++)
1267 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1270 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1271 change
.args
[0].u
.hostmask
= bd
->mask
;
1272 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1278 del_channel_ban(bd
);
1281 static void chanserv_expire_suspension(void *data
);
1284 unregister_channel(struct chanData
*channel
, const char *reason
)
1286 struct mod_chanmode change
;
1287 char msgbuf
[MAXLEN
];
1289 /* After channel unregistration, the following must be cleaned
1291 - Channel information.
1294 - Channel suspension data.
1295 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1301 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1305 mod_chanmode_init(&change
);
1306 change
.modes_clear
|= MODE_REGISTERED
;
1307 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1310 while(channel
->users
)
1311 del_channel_user(channel
->users
, 0);
1313 while(channel
->bans
)
1314 del_channel_ban(channel
->bans
);
1316 free(channel
->topic
);
1317 free(channel
->registrar
);
1318 free(channel
->greeting
);
1319 free(channel
->user_greeting
);
1320 free(channel
->topic_mask
);
1323 channel
->prev
->next
= channel
->next
;
1325 channelList
= channel
->next
;
1328 channel
->next
->prev
= channel
->prev
;
1330 if(channel
->suspended
)
1332 struct chanNode
*cNode
= channel
->channel
;
1333 struct suspended
*suspended
, *next_suspended
;
1335 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1337 next_suspended
= suspended
->previous
;
1338 free(suspended
->suspender
);
1339 free(suspended
->reason
);
1340 if(suspended
->expires
)
1341 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1346 cNode
->channel_info
= NULL
;
1348 channel
->channel
->channel_info
= NULL
;
1350 dict_delete(channel
->notes
);
1351 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1352 if(!IsSuspended(channel
))
1353 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1354 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1355 UnlockChannel(channel
->channel
);
1357 registered_channels
--;
1361 expire_channels(UNUSED_ARG(void *data
))
1363 struct chanData
*channel
, *next
;
1364 struct userData
*user
;
1365 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1367 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1368 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1370 for(channel
= channelList
; channel
; channel
= next
)
1372 next
= channel
->next
;
1374 /* See if the channel can be expired. */
1375 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1376 || IsProtected(channel
))
1379 /* Make sure there are no high-ranking users still in the channel. */
1380 for(user
=channel
->users
; user
; user
=user
->next
)
1381 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1386 /* Unregister the channel */
1387 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1388 unregister_channel(channel
, "registration expired.");
1391 if(chanserv_conf
.channel_expire_frequency
)
1392 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1396 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1398 char protect
= channel
->chOpts
[chProtect
];
1399 struct userData
*cs_victim
, *cs_aggressor
;
1401 /* Don't protect if no one is to be protected, someone is attacking
1402 himself, or if the aggressor is an IRC Operator. */
1403 if(protect
== 'n' || victim
== aggressor
|| IsOper(aggressor
))
1406 /* Don't protect if the victim isn't authenticated (because they
1407 can't be a channel user), unless we are to protect non-users
1409 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1410 if(protect
!= 'a' && !cs_victim
)
1413 /* Protect if the aggressor isn't a user because at this point,
1414 the aggressor can only be less than or equal to the victim. */
1415 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1419 /* If the aggressor was a user, then the victim can't be helped. */
1426 if(cs_victim
->access
> cs_aggressor
->access
)
1431 if(cs_victim
->access
>= cs_aggressor
->access
)
1440 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1442 struct chanData
*cData
= channel
->channel_info
;
1443 struct userData
*cs_victim
;
1445 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1446 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveOps
]))
1447 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1449 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1457 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1459 struct chanData
*cData
= channel
->channel_info
;
1460 struct userData
*cs_victim
;
1462 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1463 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveHalfOps
]))
1464 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1466 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1475 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1477 if(IsService(victim
))
1479 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1483 if(protect_user(victim
, user
, channel
->channel_info
))
1485 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1493 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1495 if(IsService(victim
))
1497 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1501 if(protect_user(victim
, user
, channel
->channel_info
))
1503 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1510 static struct do_not_register
*
1511 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1513 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1514 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1515 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1516 strcpy(dnr
->reason
, reason
);
1518 if(dnr
->chan_name
[0] == '*')
1519 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1520 else if(strpbrk(dnr
->chan_name
, "*?"))
1521 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1523 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1527 static struct dnrList
1528 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1530 struct dnrList list
;
1532 struct do_not_register
*dnr
;
1534 dnrList_init(&list
);
1535 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1536 dnrList_append(&list
, dnr
);
1537 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1538 dnrList_append(&list
, dnr
);
1540 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1541 if(match_ircglob(chan_name
, iter_key(it
)))
1542 dnrList_append(&list
, iter_data(it
));
1547 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1549 struct dnrList list
;
1550 struct do_not_register
*dnr
;
1552 char buf
[INTERVALLEN
];
1554 list
= chanserv_find_dnrs(chan_name
, handle
);
1555 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1557 dnr
= list
.list
[ii
];
1560 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1561 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1564 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1567 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1572 struct do_not_register
*
1573 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1575 struct do_not_register
*dnr
;
1578 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1582 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1584 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1585 if(match_ircglob(chan_name
, iter_key(it
)))
1586 return iter_data(it
);
1591 static CHANSERV_FUNC(cmd_noregister
)
1594 struct do_not_register
*dnr
;
1595 char buf
[INTERVALLEN
];
1596 unsigned int matches
;
1602 reply("CSMSG_DNR_SEARCH_RESULTS");
1604 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1606 dnr
= iter_data(it
);
1608 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1610 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1613 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1615 dnr
= iter_data(it
);
1617 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1619 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1622 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1624 dnr
= iter_data(it
);
1626 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1628 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1633 reply("MSG_MATCH_COUNT", matches
);
1635 reply("MSG_NO_MATCHES");
1641 if(!IsChannelName(target
) && (*target
!= '*'))
1643 reply("CSMSG_NOT_DNR", target
);
1649 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1650 if((*target
== '*') && !get_handle_info(target
+ 1))
1652 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1655 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1656 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1660 reply("CSMSG_DNR_SEARCH_RESULTS");
1662 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1664 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1666 reply("MSG_NO_MATCHES");
1670 static CHANSERV_FUNC(cmd_allowregister
)
1672 const char *chan_name
= argv
[1];
1674 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1676 dict_remove(handle_dnrs
, chan_name
+1);
1677 reply("CSMSG_DNR_REMOVED", chan_name
);
1679 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1681 dict_remove(plain_dnrs
, chan_name
);
1682 reply("CSMSG_DNR_REMOVED", chan_name
);
1684 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1686 dict_remove(mask_dnrs
, chan_name
);
1687 reply("CSMSG_DNR_REMOVED", chan_name
);
1691 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1698 chanserv_get_owned_count(struct handle_info
*hi
)
1700 struct userData
*cList
;
1703 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1704 if(cList
->access
== UL_OWNER
)
1709 static CHANSERV_FUNC(cmd_register
)
1711 struct handle_info
*handle
;
1712 struct chanData
*cData
;
1713 struct modeNode
*mn
;
1714 char reason
[MAXLEN
];
1716 unsigned int new_channel
, force
=0;
1717 struct do_not_register
*dnr
;
1721 if(channel
->channel_info
)
1723 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1727 if(channel
->bad_channel
)
1729 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1734 && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1736 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1741 chan_name
= channel
->name
;
1745 if((argc
< 2) || !IsChannelName(argv
[1]))
1747 reply("MSG_NOT_CHANNEL_NAME");
1751 if(opserv_bad_channel(argv
[1]))
1753 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1758 chan_name
= argv
[1];
1761 if(argc
>= (new_channel
+2))
1763 if(!IsHelping(user
))
1765 reply("CSMSG_PROXY_FORBIDDEN");
1769 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1771 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1772 dnr
= chanserv_is_dnr(chan_name
, handle
);
1776 handle
= user
->handle_info
;
1777 dnr
= chanserv_is_dnr(chan_name
, handle
);
1781 if(!IsHelping(user
))
1782 reply("CSMSG_DNR_CHANNEL", chan_name
);
1784 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1788 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1790 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1795 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1797 cData
= register_channel(channel
, user
->handle_info
->handle
);
1798 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1799 cData
->modes
= chanserv_conf
.default_modes
;
1801 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1802 if (IsOffChannel(cData
))
1804 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
1808 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
1809 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1810 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1812 mod_chanmode_announce(chanserv
, channel
, change
);
1813 mod_chanmode_free(change
);
1816 /* Initialize the channel's max user record. */
1817 cData
->max
= channel
->members
.used
;
1819 if(handle
!= user
->handle_info
)
1820 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1822 reply("CSMSG_REG_SUCCESS", channel
->name
);
1824 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1825 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1830 make_confirmation_string(struct userData
*uData
)
1832 static char strbuf
[16];
1837 for(src
= uData
->handle
->handle
; *src
; )
1838 accum
= accum
* 31 + toupper(*src
++);
1840 for(src
= uData
->channel
->channel
->name
; *src
; )
1841 accum
= accum
* 31 + toupper(*src
++);
1842 sprintf(strbuf
, "%08x", accum
);
1846 static CHANSERV_FUNC(cmd_unregister
)
1849 char reason
[MAXLEN
];
1850 struct chanData
*cData
;
1851 struct userData
*uData
;
1853 cData
= channel
->channel_info
;
1856 reply("CSMSG_NOT_REGISTERED", channel
->name
);
1860 uData
= GetChannelUser(cData
, user
->handle_info
);
1861 if(!uData
|| (uData
->access
< UL_OWNER
))
1863 reply("CSMSG_NO_ACCESS");
1867 if(IsProtected(cData
))
1869 reply("CSMSG_UNREG_NODELETE", channel
->name
);
1873 if(!IsHelping(user
))
1875 const char *confirm_string
;
1876 if(IsSuspended(cData
))
1878 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
1881 confirm_string
= make_confirmation_string(uData
);
1882 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
1884 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
1889 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
1890 name
= strdup(channel
->name
);
1891 unregister_channel(cData
, reason
);
1892 reply("CSMSG_UNREG_SUCCESS", name
);
1897 static CHANSERV_FUNC(cmd_move
)
1899 struct mod_chanmode change
;
1900 struct chanNode
*target
;
1901 struct modeNode
*mn
;
1902 struct userData
*uData
;
1903 char reason
[MAXLEN
];
1904 struct do_not_register
*dnr
;
1908 if(IsProtected(channel
->channel_info
))
1910 reply("CSMSG_MOVE_NODELETE", channel
->name
);
1914 if(!IsChannelName(argv
[1]))
1916 reply("MSG_NOT_CHANNEL_NAME");
1920 if(opserv_bad_channel(argv
[1]))
1922 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1926 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
1928 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
1930 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
1932 if(!IsHelping(user
))
1933 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
1935 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
1941 mod_chanmode_init(&change
);
1942 if(!(target
= GetChannel(argv
[1])))
1944 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1945 if(!IsSuspended(channel
->channel_info
))
1946 AddChannelUser(chanserv
, target
);
1948 else if(target
->channel_info
)
1950 reply("CSMSG_ALREADY_REGGED", target
->name
);
1953 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
1954 && !IsHelping(user
))
1956 reply("CSMSG_MUST_BE_OPPED", target
->name
);
1959 else if(!IsSuspended(channel
->channel_info
))
1962 change
.args
[0].mode
= MODE_CHANOP
;
1963 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
1964 mod_chanmode_announce(chanserv
, target
, &change
);
1969 /* Clear MODE_REGISTERED from old channel, add it to new. */
1971 change
.modes_clear
= MODE_REGISTERED
;
1972 mod_chanmode_announce(chanserv
, channel
, &change
);
1973 change
.modes_clear
= 0;
1974 change
.modes_set
= MODE_REGISTERED
;
1975 mod_chanmode_announce(chanserv
, target
, &change
);
1978 /* Move the channel_info to the target channel; it
1979 shouldn't be necessary to clear timeq callbacks
1980 for the old channel. */
1981 target
->channel_info
= channel
->channel_info
;
1982 target
->channel_info
->channel
= target
;
1983 channel
->channel_info
= NULL
;
1985 reply("CSMSG_MOVE_SUCCESS", target
->name
);
1987 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
1988 if(!IsSuspended(target
->channel_info
))
1990 char reason2
[MAXLEN
];
1991 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
1992 DelChannelUser(chanserv
, channel
, reason2
, 0);
1994 UnlockChannel(channel
);
1995 LockChannel(target
);
1996 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2001 merge_users(struct chanData
*source
, struct chanData
*target
)
2003 struct userData
*suData
, *tuData
, *next
;
2009 /* Insert the source's users into the scratch area. */
2010 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2011 dict_insert(merge
, suData
->handle
->handle
, suData
);
2013 /* Iterate through the target's users, looking for
2014 users common to both channels. The lower access is
2015 removed from either the scratch area or target user
2017 for(tuData
= target
->users
; tuData
; tuData
= next
)
2019 struct userData
*choice
;
2021 next
= tuData
->next
;
2023 /* If a source user exists with the same handle as a target
2024 channel's user, resolve the conflict by removing one. */
2025 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2029 /* Pick the data we want to keep. */
2030 /* If the access is the same, use the later seen time. */
2031 if(suData
->access
== tuData
->access
)
2032 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2033 else /* Otherwise, keep the higher access level. */
2034 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2036 /* Remove the user that wasn't picked. */
2037 if(choice
== tuData
)
2039 dict_remove(merge
, suData
->handle
->handle
);
2040 del_channel_user(suData
, 0);
2043 del_channel_user(tuData
, 0);
2046 /* Move the remaining users to the target channel. */
2047 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2049 suData
= iter_data(it
);
2051 /* Insert the user into the target channel's linked list. */
2052 suData
->prev
= NULL
;
2053 suData
->next
= target
->users
;
2054 suData
->channel
= target
;
2057 target
->users
->prev
= suData
;
2058 target
->users
= suData
;
2060 /* Update the user counts for the target channel; the
2061 source counts are left alone. */
2062 target
->userCount
++;
2065 /* Possible to assert (source->users == NULL) here. */
2066 source
->users
= NULL
;
2071 merge_bans(struct chanData
*source
, struct chanData
*target
)
2073 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2075 /* Hold on to the original head of the target ban list
2076 to avoid comparing source bans with source bans. */
2077 tFront
= target
->bans
;
2079 /* Perform a totally expensive O(n*m) merge, ick. */
2080 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2082 /* Flag to track whether the ban's been moved
2083 to the destination yet. */
2086 /* Possible to assert (sbData->prev == NULL) here. */
2087 sNext
= sbData
->next
;
2089 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2091 tNext
= tbData
->next
;
2093 /* Perform two comparisons between each source
2094 and target ban, conflicts are resolved by
2095 keeping the broader ban and copying the later
2096 expiration and triggered time. */
2097 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2099 /* There is a broader ban in the target channel that
2100 overrides one in the source channel; remove the
2101 source ban and break. */
2102 if(sbData
->expires
> tbData
->expires
)
2103 tbData
->expires
= sbData
->expires
;
2104 if(sbData
->triggered
> tbData
->triggered
)
2105 tbData
->triggered
= sbData
->triggered
;
2106 del_channel_ban(sbData
);
2109 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2111 /* There is a broader ban in the source channel that
2112 overrides one in the target channel; remove the
2113 target ban, fall through and move the source over. */
2114 if(tbData
->expires
> sbData
->expires
)
2115 sbData
->expires
= tbData
->expires
;
2116 if(tbData
->triggered
> sbData
->triggered
)
2117 sbData
->triggered
= tbData
->triggered
;
2118 if(tbData
== tFront
)
2120 del_channel_ban(tbData
);
2123 /* Source bans can override multiple target bans, so
2124 we allow a source to run through this loop multiple
2125 times, but we can only move it once. */
2130 /* Remove the source ban from the source ban list. */
2132 sbData
->next
->prev
= sbData
->prev
;
2134 /* Modify the source ban's associated channel. */
2135 sbData
->channel
= target
;
2137 /* Insert the ban into the target channel's linked list. */
2138 sbData
->prev
= NULL
;
2139 sbData
->next
= target
->bans
;
2142 target
->bans
->prev
= sbData
;
2143 target
->bans
= sbData
;
2145 /* Update the user counts for the target channel. */
2150 /* Possible to assert (source->bans == NULL) here. */
2151 source
->bans
= NULL
;
2155 merge_data(struct chanData
*source
, struct chanData
*target
)
2157 if(source
->visited
> target
->visited
)
2158 target
->visited
= source
->visited
;
2162 merge_channel(struct chanData
*source
, struct chanData
*target
)
2164 merge_users(source
, target
);
2165 merge_bans(source
, target
);
2166 merge_data(source
, target
);
2169 static CHANSERV_FUNC(cmd_merge
)
2171 struct userData
*target_user
;
2172 struct chanNode
*target
;
2173 char reason
[MAXLEN
];
2177 /* Make sure the target channel exists and is registered to the user
2178 performing the command. */
2179 if(!(target
= GetChannel(argv
[1])))
2181 reply("MSG_INVALID_CHANNEL");
2185 if(!target
->channel_info
)
2187 reply("CSMSG_NOT_REGISTERED", target
->name
);
2191 if(IsProtected(channel
->channel_info
))
2193 reply("CSMSG_MERGE_NODELETE");
2197 if(IsSuspended(target
->channel_info
))
2199 reply("CSMSG_MERGE_SUSPENDED");
2203 if(channel
== target
)
2205 reply("CSMSG_MERGE_SELF");
2209 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2210 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2212 reply("CSMSG_MERGE_NOT_OWNER");
2216 /* Merge the channel structures and associated data. */
2217 merge_channel(channel
->channel_info
, target
->channel_info
);
2218 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2219 unregister_channel(channel
->channel_info
, reason
);
2220 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2224 static CHANSERV_FUNC(cmd_opchan
)
2226 struct mod_chanmode change
;
2227 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2229 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2232 channel
->channel_info
->may_opchan
= 0;
2233 mod_chanmode_init(&change
);
2235 change
.args
[0].mode
= MODE_CHANOP
;
2236 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2237 mod_chanmode_announce(chanserv
, channel
, &change
);
2238 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2242 static CHANSERV_FUNC(cmd_adduser
)
2244 struct userData
*actee
;
2245 struct userData
*actor
;
2246 struct handle_info
*handle
;
2247 unsigned short access
;
2251 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2253 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2257 access
= user_level_from_name(argv
[2], UL_OWNER
);
2260 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2264 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2265 if(actor
->access
<= access
)
2267 reply("CSMSG_NO_BUMP_ACCESS");
2271 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2274 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2276 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, actee
->access
);
2280 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2281 scan_user_presence(actee
, NULL
);
2282 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, access
);
2286 static CHANSERV_FUNC(cmd_clvl
)
2288 struct handle_info
*handle
;
2289 struct userData
*victim
;
2290 struct userData
*actor
;
2291 unsigned short new_access
;
2292 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2296 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2298 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2301 if(handle
== user
->handle_info
&& !privileged
)
2303 reply("CSMSG_NO_SELF_CLVL");
2307 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2309 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2313 if(actor
->access
<= victim
->access
&& !privileged
)
2315 reply("MSG_USER_OUTRANKED", handle
->handle
);
2319 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2323 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2327 if(new_access
>= actor
->access
&& !privileged
)
2329 reply("CSMSG_NO_BUMP_ACCESS");
2333 victim
->access
= new_access
;
2334 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, new_access
, channel
->name
);
2338 static CHANSERV_FUNC(cmd_deluser
)
2340 struct handle_info
*handle
;
2341 struct userData
*victim
;
2342 struct userData
*actor
;
2343 unsigned short access
;
2348 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2350 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2353 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2355 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2361 access
= user_level_from_name(argv
[1], UL_OWNER
);
2364 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2367 if(access
!= victim
->access
)
2369 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, victim
->access
, argv
[1]);
2375 access
= victim
->access
;
2378 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2380 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2384 chan_name
= strdup(channel
->name
);
2385 del_channel_user(victim
, 1);
2386 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2392 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2394 struct userData
*actor
, *uData
, *next
;
2396 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2398 if(min_access
> max_access
)
2400 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2404 if((actor
->access
<= max_access
) && !IsHelping(user
))
2406 reply("CSMSG_NO_ACCESS");
2410 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2414 if((uData
->access
>= min_access
)
2415 && (uData
->access
<= max_access
)
2416 && match_ircglob(uData
->handle
->handle
, mask
))
2417 del_channel_user(uData
, 1);
2420 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2424 static CHANSERV_FUNC(cmd_mdelowner
)
2426 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2429 static CHANSERV_FUNC(cmd_mdelcoowner
)
2431 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2434 static CHANSERV_FUNC(cmd_mdelmanager
)
2436 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2439 static CHANSERV_FUNC(cmd_mdelop
)
2441 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2444 static CHANSERV_FUNC(cmd_mdelpeon
)
2446 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2449 static CHANSERV_FUNC(cmd_mdelhalfop
)
2451 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2456 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2458 struct banData
*bData
, *next
;
2459 char interval
[INTERVALLEN
];
2464 limit
= now
- duration
;
2465 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2469 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2472 del_channel_ban(bData
);
2476 intervalString(interval
, duration
, user
->handle_info
);
2477 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2482 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2484 struct userData
*actor
, *uData
, *next
;
2485 char interval
[INTERVALLEN
];
2489 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2490 if(min_access
> max_access
)
2492 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2496 if((actor
->access
<= max_access
) && !IsHelping(user
))
2498 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2503 limit
= now
- duration
;
2504 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2508 if((uData
->seen
> limit
) || uData
->present
)
2511 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2512 || (!max_access
&& (uData
->access
< actor
->access
)))
2514 del_channel_user(uData
, 1);
2522 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2524 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2528 static CHANSERV_FUNC(cmd_trim
)
2530 unsigned long duration
;
2531 unsigned short min_level
, max_level
;
2535 duration
= ParseInterval(argv
[2]);
2538 reply("CSMSG_CANNOT_TRIM");
2542 if(!irccasecmp(argv
[1], "bans"))
2544 cmd_trim_bans(user
, channel
, duration
);
2547 else if(!irccasecmp(argv
[1], "users"))
2549 cmd_trim_users(user
, channel
, 0, 0, duration
);
2552 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2554 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2557 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2559 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2564 reply("CSMSG_INVALID_TRIM", argv
[1]);
2569 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2570 to the user. cmd_all takes advantage of this. */
2571 static CHANSERV_FUNC(cmd_up
)
2573 struct mod_chanmode change
;
2574 struct userData
*uData
;
2577 mod_chanmode_init(&change
);
2579 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2580 if(!change
.args
[0].u
.member
)
2583 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2587 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2591 reply("CSMSG_GODMODE_UP", argv
[0]);
2594 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveOps
])
2596 change
.args
[0].mode
= MODE_CHANOP
;
2597 errmsg
= "CSMSG_ALREADY_OPPED";
2599 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
2601 change
.args
[0].mode
= MODE_HALFOP
;
2602 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2604 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveVoice
])
2606 change
.args
[0].mode
= MODE_VOICE
;
2607 errmsg
= "CSMSG_ALREADY_VOICED";
2612 reply("CSMSG_NO_ACCESS");
2615 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2616 if(!change
.args
[0].mode
)
2619 reply(errmsg
, channel
->name
);
2622 modcmd_chanmode_announce(&change
);
2626 static CHANSERV_FUNC(cmd_down
)
2628 struct mod_chanmode change
;
2630 mod_chanmode_init(&change
);
2632 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2633 if(!change
.args
[0].u
.member
)
2636 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2640 if(!change
.args
[0].u
.member
->modes
)
2643 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2647 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2648 modcmd_chanmode_announce(&change
);
2652 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
)
2654 struct userData
*cList
;
2656 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2658 if(IsSuspended(cList
->channel
)
2659 || IsUserSuspended(cList
)
2660 || !GetUserMode(cList
->channel
->channel
, user
))
2663 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2669 static CHANSERV_FUNC(cmd_upall
)
2671 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2674 static CHANSERV_FUNC(cmd_downall
)
2676 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2679 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2680 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2683 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
)
2685 unsigned int ii
, valid
;
2686 struct userNode
*victim
;
2687 struct mod_chanmode
*change
;
2689 change
= mod_chanmode_alloc(argc
- 1);
2691 for(ii
=valid
=0; ++ii
< argc
; )
2693 if(!(victim
= GetUserH(argv
[ii
])))
2695 change
->args
[valid
].mode
= mode
;
2696 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2697 if(!change
->args
[valid
].u
.member
)
2699 if(validate
&& !validate(user
, channel
, victim
))
2704 change
->argc
= valid
;
2705 if(valid
< (argc
-1))
2706 reply("CSMSG_PROCESS_FAILED");
2709 modcmd_chanmode_announce(change
);
2710 reply(action
, channel
->name
);
2712 mod_chanmode_free(change
);
2716 static CHANSERV_FUNC(cmd_op
)
2718 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2721 static CHANSERV_FUNC(cmd_hop
)
2723 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2726 static CHANSERV_FUNC(cmd_deop
)
2728 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2731 static CHANSERV_FUNC(cmd_dehop
)
2733 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2736 static CHANSERV_FUNC(cmd_voice
)
2738 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2741 static CHANSERV_FUNC(cmd_devoice
)
2743 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2747 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2753 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2755 struct modeNode
*mn
= channel
->members
.list
[ii
];
2757 if(IsService(mn
->user
))
2760 if(!user_matches_glob(mn
->user
, ban
, 1))
2763 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2767 victims
[(*victimCount
)++] = mn
;
2773 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2775 struct userNode
*victim
;
2776 struct modeNode
**victims
;
2777 unsigned int offset
, n
, victimCount
, duration
= 0;
2778 char *reason
= "Bye.", *ban
, *name
;
2779 char interval
[INTERVALLEN
];
2781 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2782 REQUIRE_PARAMS(offset
);
2785 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2786 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2788 /* Truncate the reason to a length of TOPICLEN, as
2789 the ircd does; however, leave room for an ellipsis
2790 and the kicker's nick. */
2791 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2795 if((victim
= GetUserH(argv
[1])))
2797 victims
= alloca(sizeof(victims
[0]));
2798 victims
[0] = GetUserMode(channel
, victim
);
2799 /* XXX: The comparison with ACTION_KICK is just because all
2800 * other actions can work on users outside the channel, and we
2801 * want to allow those (e.g. unbans) in that case. If we add
2802 * some other ejection action for in-channel users, change
2804 victimCount
= victims
[0] ? 1 : 0;
2806 if(IsService(victim
))
2808 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
2812 if((action
== ACTION_KICK
) && !victimCount
)
2814 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
2818 if(protect_user(victim
, user
, channel
->channel_info
))
2820 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2824 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
2825 name
= victim
->nick
;
2829 if(!is_ircmask(argv
[1]))
2831 reply("MSG_NICK_UNKNOWN", argv
[1]);
2835 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
2837 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
2839 reply("CSMSG_MASK_PROTECTED", argv
[1]);
2842 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
2844 victimCount
= 0; /* Dont deop etc ppl who match this */
2846 #ifdef entropy_lameness
2847 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
2849 reply("CSMSG_LAME_MASK", argv
[1]);
2854 if((action
== ACTION_KICK
) && (victimCount
== 0))
2856 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
2860 name
= ban
= strdup(argv
[1]);
2863 /* Truncate the ban in place if necessary; we must ensure
2864 that 'ban' is a valid ban mask before sanitizing it. */
2865 sanitize_ircmask(ban
);
2867 if(action
& ACTION_ADD_BAN
)
2869 struct banData
*bData
, *next
;
2871 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
2873 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
2878 if(action
& ACTION_ADD_TIMED_BAN
)
2880 duration
= ParseInterval(argv
[2]);
2884 reply("CSMSG_DURATION_TOO_LOW");
2888 else if(duration
> (86400 * 365 * 2))
2890 reply("CSMSG_DURATION_TOO_HIGH");
2896 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2898 if(match_ircglobs(bData
->mask
, ban
))
2900 int exact
= !irccasecmp(bData
->mask
, ban
);
2902 /* The ban is redundant; there is already a ban
2903 with the same effect in place. */
2907 free(bData
->reason
);
2908 bData
->reason
= strdup(reason
);
2909 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
2911 reply("CSMSG_REASON_CHANGE", ban
);
2915 if(exact
&& bData
->expires
)
2919 /* If the ban matches an existing one exactly,
2920 extend the expiration time if the provided
2921 duration is longer. */
2922 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
2924 bData
->expires
= now
+ duration
;
2935 /* Delete the expiration timeq entry and
2936 requeue if necessary. */
2937 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
2940 timeq_add(bData
->expires
, expire_ban
, bData
);
2944 /* automated kickban */
2947 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
2949 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2955 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2962 if(match_ircglobs(ban
, bData
->mask
))
2964 /* The ban we are adding makes previously existing
2965 bans redundant; silently remove them. */
2966 del_channel_ban(bData
);
2970 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
);
2972 name
= ban
= strdup(bData
->mask
);
2976 /* WHAT DOES THIS DO?? -Rubin */
2977 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
2979 extern const char *hidden_host_suffix
;
2980 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
2982 unsigned int l1
, l2
;
2985 l2
= strlen(old_name
);
2988 if(irccasecmp(ban
+ l1
- l2
, old_name
))
2990 new_mask
= malloc(MAXLEN
);
2991 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
2993 name
= ban
= new_mask
;
2998 if(action
& ACTION_BAN
)
3000 unsigned int exists
;
3001 struct mod_chanmode
*change
;
3003 if(channel
->banlist
.used
>= MAXBANS
)
3006 reply("CSMSG_BANLIST_FULL", channel
->name
);
3011 exists
= ChannelBanExists(channel
, ban
);
3012 change
= mod_chanmode_alloc(victimCount
+ 1);
3013 for(n
= 0; n
< victimCount
; ++n
)
3015 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3016 change
->args
[n
].u
.member
= victims
[n
];
3020 change
->args
[n
].mode
= MODE_BAN
;
3021 change
->args
[n
++].u
.hostmask
= ban
;
3025 modcmd_chanmode_announce(change
);
3027 mod_chanmode_announce(chanserv
, channel
, change
);
3028 mod_chanmode_free(change
);
3030 if(exists
&& (action
== ACTION_BAN
))
3033 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3039 if(action
& ACTION_KICK
)
3041 char kick_reason
[MAXLEN
];
3042 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3044 for(n
= 0; n
< victimCount
; n
++)
3045 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3050 /* No response, since it was automated. */
3052 else if(action
& ACTION_ADD_BAN
)
3055 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3057 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3059 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3060 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3061 else if(action
& ACTION_BAN
)
3062 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3063 else if(action
& ACTION_KICK
&& victimCount
)
3064 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3070 static CHANSERV_FUNC(cmd_kickban
)
3072 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3075 static CHANSERV_FUNC(cmd_kick
)
3077 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3080 static CHANSERV_FUNC(cmd_ban
)
3082 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3085 static CHANSERV_FUNC(cmd_addban
)
3087 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
3090 static CHANSERV_FUNC(cmd_addtimedban
)
3092 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
3095 static struct mod_chanmode
*
3096 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3098 struct mod_chanmode
*change
;
3099 unsigned char *match
;
3100 unsigned int ii
, count
;
3102 match
= alloca(bans
->used
);
3105 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3107 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3114 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3116 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3123 change
= mod_chanmode_alloc(count
);
3124 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3128 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3129 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3131 assert(count
== change
->argc
);
3136 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3138 struct userNode
*actee
;
3144 /* may want to allow a comma delimited list of users... */
3145 if(!(actee
= GetUserH(argv
[1])))
3147 if(!is_ircmask(argv
[1]))
3149 reply("MSG_NICK_UNKNOWN", argv
[1]);
3153 mask
= strdup(argv
[1]);
3156 /* We don't sanitize the mask here because ircu
3158 if(action
& ACTION_UNBAN
)
3160 struct mod_chanmode
*change
;
3161 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3166 for(ii
= 0; ii
< change
->argc
; ++ii
)
3167 free((char*)change
->args
[ii
].u
.hostmask
);
3168 modcmd_chanmode_announce(change
);
3169 mod_chanmode_free(change
);
3174 if(action
& ACTION_DEL_BAN
)
3176 struct banData
*ban
, *next
;
3178 ban
= channel
->channel_info
->bans
;
3182 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3185 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3190 del_channel_ban(ban
);
3197 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3199 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3205 static CHANSERV_FUNC(cmd_unban
)
3207 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3210 static CHANSERV_FUNC(cmd_delban
)
3212 /* it doesn't necessarily have to remove the channel ban - may want
3213 to make that an option. */
3214 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3217 static CHANSERV_FUNC(cmd_unbanme
)
3219 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3220 long flags
= ACTION_UNBAN
;
3222 /* remove permanent bans if the user has the proper access. */
3223 if(uData
->access
>= UL_MANAGER
)
3224 flags
|= ACTION_DEL_BAN
;
3226 argv
[1] = user
->nick
;
3227 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3230 static CHANSERV_FUNC(cmd_unbanall
)
3232 struct mod_chanmode
*change
;
3235 if(!channel
->banlist
.used
)
3237 reply("CSMSG_NO_BANS", channel
->name
);
3241 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3242 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3244 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3245 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3247 modcmd_chanmode_announce(change
);
3248 for(ii
= 0; ii
< change
->argc
; ++ii
)
3249 free((char*)change
->args
[ii
].u
.hostmask
);
3250 mod_chanmode_free(change
);
3251 reply("CSMSG_BANS_REMOVED", channel
->name
);
3255 static CHANSERV_FUNC(cmd_open
)
3257 struct mod_chanmode
*change
;
3260 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3262 change
= mod_chanmode_alloc(0);
3263 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3264 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3265 && channel
->channel_info
->modes
.modes_set
)
3266 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3267 modcmd_chanmode_announce(change
);
3268 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3269 for(ii
= 0; ii
< change
->argc
; ++ii
)
3270 free((char*)change
->args
[ii
].u
.hostmask
);
3271 mod_chanmode_free(change
);
3275 static CHANSERV_FUNC(cmd_myaccess
)
3277 static struct string_buffer sbuf
;
3278 struct handle_info
*target_handle
;
3279 struct userData
*uData
;
3282 target_handle
= user
->handle_info
;
3283 else if(!IsHelping(user
))
3285 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3288 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3291 if(!target_handle
->channels
)
3293 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3297 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3298 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3300 struct chanData
*cData
= uData
->channel
;
3302 if(uData
->access
> UL_OWNER
)
3304 if(IsProtected(cData
)
3305 && (target_handle
!= user
->handle_info
)
3306 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3309 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3310 if(uData
->flags
!= USER_AUTO_OP
)
3311 string_buffer_append(&sbuf
, ',');
3312 if(IsUserSuspended(uData
))
3313 string_buffer_append(&sbuf
, 's');
3314 if(IsUserAutoOp(uData
))
3316 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
3317 string_buffer_append(&sbuf
, 'o');
3318 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
3319 string_buffer_append(&sbuf
, 'h');
3320 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
3321 string_buffer_append(&sbuf
, 'v');
3323 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3324 string_buffer_append(&sbuf
, 'i');
3326 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3328 string_buffer_append_string(&sbuf
, ")]");
3329 string_buffer_append(&sbuf
, '\0');
3330 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3336 static CHANSERV_FUNC(cmd_access
)
3338 struct userNode
*target
;
3339 struct handle_info
*target_handle
;
3340 struct userData
*uData
;
3342 char prefix
[MAXLEN
];
3347 target_handle
= target
->handle_info
;
3349 else if((target
= GetUserH(argv
[1])))
3351 target_handle
= target
->handle_info
;
3353 else if(argv
[1][0] == '*')
3355 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3357 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3363 reply("MSG_NICK_UNKNOWN", argv
[1]);
3367 assert(target
|| target_handle
);
3369 if(target
== chanserv
)
3371 reply("CSMSG_IS_CHANSERV");
3379 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3384 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3387 reply("MSG_AUTHENTICATE");
3393 const char *epithet
= NULL
, *type
= NULL
;
3396 epithet
= chanserv_conf
.irc_operator_epithet
;
3399 else if(IsNetworkHelper(target
))
3401 epithet
= chanserv_conf
.network_helper_epithet
;
3402 type
= "network helper";
3404 else if(IsSupportHelper(target
))
3406 epithet
= chanserv_conf
.support_helper_epithet
;
3407 type
= "support helper";
3411 if(target_handle
->epithet
)
3412 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3414 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3416 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3420 sprintf(prefix
, "%s", target_handle
->handle
);
3423 if(!channel
->channel_info
)
3425 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3429 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3430 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3431 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3433 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, uData
->access
, channel
->name
);
3434 /* To prevent possible information leaks, only show infolines
3435 * if the requestor is in the channel or it's their own
3437 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3439 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3441 /* Likewise, only say it's suspended if the user has active
3442 * access in that channel or it's their own entry. */
3443 if(IsUserSuspended(uData
)
3444 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3445 || (user
->handle_info
== uData
->handle
)))
3447 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3452 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3459 zoot_list(struct listData
*list
)
3461 struct userData
*uData
;
3462 unsigned int start
, curr
, highest
, lowest
;
3463 struct helpfile_table tmp_table
;
3464 const char **temp
, *msg
;
3466 if(list
->table
.length
== 1)
3469 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3471 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3472 msg
= user_find_message(list
->user
, "MSG_NONE");
3473 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3475 tmp_table
.width
= list
->table
.width
;
3476 tmp_table
.flags
= list
->table
.flags
;
3477 list
->table
.contents
[0][0] = " ";
3478 highest
= list
->highest
;
3479 if(list
->lowest
!= 0)
3480 lowest
= list
->lowest
;
3481 else if(highest
< 100)
3484 lowest
= highest
- 100;
3485 for(start
= curr
= 1; curr
< list
->table
.length
; )
3487 uData
= list
->users
[curr
-1];
3488 list
->table
.contents
[curr
++][0] = " ";
3489 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3492 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, lowest
, highest
, list
->search
);
3494 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, lowest
, highest
);
3495 temp
= list
->table
.contents
[--start
];
3496 list
->table
.contents
[start
] = list
->table
.contents
[0];
3497 tmp_table
.contents
= list
->table
.contents
+ start
;
3498 tmp_table
.length
= curr
- start
;
3499 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3500 list
->table
.contents
[start
] = temp
;
3502 highest
= lowest
- 1;
3503 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3509 def_list(struct listData
*list
)
3513 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3515 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3516 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3517 if(list
->table
.length
== 1)
3519 msg
= user_find_message(list
->user
, "MSG_NONE");
3520 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3525 userData_access_comp(const void *arg_a
, const void *arg_b
)
3527 const struct userData
*a
= *(struct userData
**)arg_a
;
3528 const struct userData
*b
= *(struct userData
**)arg_b
;
3530 if(a
->access
!= b
->access
)
3531 res
= b
->access
- a
->access
;
3533 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3538 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3540 void (*send_list
)(struct listData
*);
3541 struct userData
*uData
;
3542 struct listData lData
;
3543 unsigned int matches
;
3547 lData
.bot
= cmd
->parent
->bot
;
3548 lData
.channel
= channel
;
3549 lData
.lowest
= lowest
;
3550 lData
.highest
= highest
;
3551 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3552 send_list
= def_list
;
3553 (void)zoot_list
; /* since it doesn't show user levels */
3555 if(user
->handle_info
)
3557 switch(user
->handle_info
->userlist_style
)
3559 case HI_STYLE_DEF
: send_list
= def_list
; break;
3560 case HI_STYLE_ZOOT
: send_list
= def_list
; break;
3564 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3566 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3568 if((uData
->access
< lowest
)
3569 || (uData
->access
> highest
)
3570 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3572 lData
.users
[matches
++] = uData
;
3574 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3576 lData
.table
.length
= matches
+1;
3577 lData
.table
.width
= 4;
3578 lData
.table
.flags
= TABLE_NO_FREE
;
3579 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3580 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3581 lData
.table
.contents
[0] = ary
;
3584 ary
[2] = "Last Seen";
3586 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3588 struct userData
*uData
= lData
.users
[matches
-1];
3589 char seen
[INTERVALLEN
];
3591 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3592 lData
.table
.contents
[matches
] = ary
;
3593 ary
[0] = strtab(uData
->access
);
3594 ary
[1] = uData
->handle
->handle
;
3597 else if(!uData
->seen
)
3600 ary
[2] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3601 ary
[2] = strdup(ary
[2]);
3602 if(IsUserSuspended(uData
))
3603 ary
[3] = "Suspended";
3604 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3605 ary
[3] = "Vacation";
3610 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3612 free((char*)lData
.table
.contents
[matches
][2]);
3613 free(lData
.table
.contents
[matches
]);
3615 free(lData
.table
.contents
[0]);
3616 free(lData
.table
.contents
);
3620 static CHANSERV_FUNC(cmd_users
)
3622 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3625 static CHANSERV_FUNC(cmd_wlist
)
3627 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3630 static CHANSERV_FUNC(cmd_clist
)
3632 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3635 static CHANSERV_FUNC(cmd_mlist
)
3637 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3640 static CHANSERV_FUNC(cmd_olist
)
3642 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3645 static CHANSERV_FUNC(cmd_hlist
)
3647 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3650 static CHANSERV_FUNC(cmd_plist
)
3652 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3655 static CHANSERV_FUNC(cmd_bans
)
3657 struct helpfile_table tbl
;
3658 unsigned int matches
= 0, timed
= 0, ii
;
3659 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3660 const char *msg_never
, *triggered
, *expires
;
3661 struct banData
*ban
, **bans
;
3668 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3670 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3672 if(search
&& !match_ircglobs(search
, ban
->mask
))
3674 bans
[matches
++] = ban
;
3679 tbl
.length
= matches
+ 1;
3680 tbl
.width
= 4 + timed
;
3682 tbl
.flags
= TABLE_NO_FREE
;
3683 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3684 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3685 tbl
.contents
[0][0] = "Mask";
3686 tbl
.contents
[0][1] = "Set By";
3687 tbl
.contents
[0][2] = "Triggered";
3690 tbl
.contents
[0][3] = "Expires";
3691 tbl
.contents
[0][4] = "Reason";
3694 tbl
.contents
[0][3] = "Reason";
3697 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3699 free(tbl
.contents
[0]);
3704 msg_never
= user_find_message(user
, "MSG_NEVER");
3705 for(ii
= 0; ii
< matches
; )
3711 else if(ban
->expires
)
3712 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3714 expires
= msg_never
;
3717 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3719 triggered
= msg_never
;
3721 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3722 tbl
.contents
[ii
][0] = ban
->mask
;
3723 tbl
.contents
[ii
][1] = ban
->owner
;
3724 tbl
.contents
[ii
][2] = strdup(triggered
);
3727 tbl
.contents
[ii
][3] = strdup(expires
);
3728 tbl
.contents
[ii
][4] = ban
->reason
;
3731 tbl
.contents
[ii
][3] = ban
->reason
;
3733 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3734 reply("MSG_MATCH_COUNT", matches
);
3735 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3737 free((char*)tbl
.contents
[ii
][2]);
3739 free((char*)tbl
.contents
[ii
][3]);
3740 free(tbl
.contents
[ii
]);
3742 free(tbl
.contents
[0]);
3748 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3750 struct chanData
*cData
= channel
->channel_info
;
3751 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3753 if(cData
->topic_mask
)
3754 return !match_ircglob(new_topic
, cData
->topic_mask
);
3755 else if(cData
->topic
)
3756 return irccasecmp(new_topic
, cData
->topic
);
3761 static CHANSERV_FUNC(cmd_topic
)
3763 struct chanData
*cData
;
3766 cData
= channel
->channel_info
;
3771 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
3772 reply("CSMSG_TOPIC_SET", cData
->topic
);
3776 reply("CSMSG_NO_TOPIC", channel
->name
);
3780 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
3781 /* If they say "!topic *", use an empty topic. */
3782 if((topic
[0] == '*') && (topic
[1] == 0))
3784 if(bad_topic(channel
, user
, topic
))
3786 char *topic_mask
= cData
->topic_mask
;
3789 char new_topic
[TOPICLEN
+1], tchar
;
3790 int pos
=0, starpos
=-1, dpos
=0, len
;
3792 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3799 len
= strlen(topic
);
3800 if((dpos
+ len
) > TOPICLEN
)
3801 len
= TOPICLEN
+ 1 - dpos
;
3802 memcpy(new_topic
+dpos
, topic
, len
);
3806 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
3807 default: new_topic
[dpos
++] = tchar
; break;
3810 if((dpos
> TOPICLEN
) || tchar
)
3813 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
3814 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
3817 new_topic
[dpos
] = 0;
3818 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
3820 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
3825 SetChannelTopic(channel
, chanserv
, topic
, 1);
3827 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
3829 /* Grab the topic and save it as the default topic. */
3831 cData
->topic
= strdup(channel
->topic
);
3837 static CHANSERV_FUNC(cmd_mode
)
3839 struct mod_chanmode
*change
;
3843 change
= &channel
->channel_info
->modes
;
3844 if(change
->modes_set
|| change
->modes_clear
) {
3845 modcmd_chanmode_announce(change
);
3846 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
3848 reply("CSMSG_NO_MODES", channel
->name
);
3852 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
3855 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
3859 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3860 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
3863 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
3864 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
3868 modcmd_chanmode_announce(change
);
3869 mod_chanmode_free(change
);
3870 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
3874 static CHANSERV_FUNC(cmd_invite
)
3876 struct userData
*uData
;
3877 struct userNode
*invite
;
3879 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3883 if(!(invite
= GetUserH(argv
[1])))
3885 reply("MSG_NICK_UNKNOWN", argv
[1]);
3892 if(GetUserMode(channel
, invite
))
3894 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
3902 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
3903 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
3906 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
3908 irc_invite(chanserv
, invite
, channel
);
3910 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
3915 static CHANSERV_FUNC(cmd_inviteme
)
3917 if(GetUserMode(channel
, user
))
3919 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
3922 if(channel
->channel_info
3923 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
3925 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
3928 irc_invite(cmd
->parent
->bot
, user
, channel
);
3933 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
3936 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
3938 /* We display things based on two dimensions:
3939 * - Issue time: present or absent
3940 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3941 * (in order of precedence, so something both expired and revoked
3942 * only counts as revoked)
3944 combo
= (suspended
->issued
? 4 : 0)
3945 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
3947 case 0: /* no issue time, indefinite expiration */
3948 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
3950 case 1: /* no issue time, expires in future */
3951 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
3952 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
3954 case 2: /* no issue time, expired */
3955 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
3956 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
3958 case 3: /* no issue time, revoked */
3959 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
3960 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
3962 case 4: /* issue time set, indefinite expiration */
3963 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3964 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
3966 case 5: /* issue time set, expires in future */
3967 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3968 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
3969 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3971 case 6: /* issue time set, expired */
3972 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3973 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
3974 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3976 case 7: /* issue time set, revoked */
3977 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3978 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
3979 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3982 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
3987 static CHANSERV_FUNC(cmd_info
)
3989 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
3990 struct userData
*uData
, *owner
;
3991 struct chanData
*cData
;
3992 struct do_not_register
*dnr
;
3997 cData
= channel
->channel_info
;
3998 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4000 uData
= GetChannelUser(cData
, user
->handle_info
);
4001 if(uData
&& (uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4003 mod_chanmode_format(&cData
->modes
, modes
);
4004 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4005 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4008 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4012 note
= iter_data(it
);
4013 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4016 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4017 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4020 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4021 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4022 if(owner
->access
== UL_OWNER
)
4023 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4024 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4025 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
4026 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4027 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4029 privileged
= IsStaff(user
);
4030 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4031 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4033 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4034 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4036 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4038 struct suspended
*suspended
;
4039 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4040 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4041 show_suspension_info(cmd
, user
, suspended
);
4043 else if(IsSuspended(cData
))
4045 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4046 show_suspension_info(cmd
, user
, cData
->suspended
);
4051 static CHANSERV_FUNC(cmd_netinfo
)
4053 extern time_t boot_time
;
4054 extern unsigned long burst_length
;
4055 char interval
[INTERVALLEN
];
4057 reply("CSMSG_NETWORK_INFO");
4058 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4059 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4060 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4061 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4062 reply("CSMSG_NETWORK_BANS", banCount
);
4063 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4064 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4065 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4070 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4072 struct helpfile_table table
;
4074 struct userNode
*user
;
4079 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4080 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4081 for(nn
=0; nn
<list
->used
; nn
++)
4083 user
= list
->list
[nn
];
4084 if(user
->modes
& skip_flags
)
4088 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4091 nick
= alloca(strlen(user
->nick
)+3);
4092 sprintf(nick
, "(%s)", user
->nick
);
4096 table
.contents
[table
.length
][0] = nick
;
4099 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4102 static CHANSERV_FUNC(cmd_ircops
)
4104 reply("CSMSG_STAFF_OPERS");
4105 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4109 static CHANSERV_FUNC(cmd_helpers
)
4111 reply("CSMSG_STAFF_HELPERS");
4112 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4116 static CHANSERV_FUNC(cmd_staff
)
4118 reply("CSMSG_NETWORK_STAFF");
4119 cmd_ircops(CSFUNC_ARGS
);
4120 cmd_helpers(CSFUNC_ARGS
);
4124 static CHANSERV_FUNC(cmd_peek
)
4126 struct modeNode
*mn
;
4127 char modes
[MODELEN
];
4129 struct helpfile_table table
;
4131 irc_make_chanmode(channel
, modes
);
4133 reply("CSMSG_PEEK_INFO", channel
->name
);
4134 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4135 reply("CSMSG_PEEK_MODES", modes
);
4136 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4140 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4141 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4142 for(n
= 0; n
< channel
->members
.used
; n
++)
4144 mn
= channel
->members
.list
[n
];
4145 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4147 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4148 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4153 reply("CSMSG_PEEK_OPS");
4154 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4157 reply("CSMSG_PEEK_NO_OPS");
4161 static MODCMD_FUNC(cmd_wipeinfo
)
4163 struct handle_info
*victim
;
4164 struct userData
*ud
, *actor
;
4167 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4168 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4170 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4172 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4175 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4177 reply("MSG_USER_OUTRANKED", victim
->handle
);
4183 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4187 static CHANSERV_FUNC(cmd_resync
)
4189 struct mod_chanmode
*changes
;
4190 struct chanData
*cData
= channel
->channel_info
;
4191 unsigned int ii
, used
;
4193 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4194 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4196 struct modeNode
*mn
= channel
->members
.list
[ii
];
4197 struct userData
*uData
;
4199 if(IsService(mn
->user
))
4202 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4203 if(!cData
->lvlOpts
[lvlGiveOps
]
4204 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4206 if(!(mn
->modes
& MODE_CHANOP
))
4208 changes
->args
[used
].mode
= MODE_CHANOP
;
4209 changes
->args
[used
++].u
.member
= mn
;
4212 else if(!cData
->lvlOpts
[lvlGiveHalfOps
]
4213 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
]))
4215 if(!(mn
->modes
& MODE_HALFOP
))
4217 changes
->args
[used
].mode
= MODE_HALFOP
;
4218 changes
->args
[used
++].u
.member
= mn
;
4220 if(mn
->modes
& MODE_CHANOP
)
4222 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4223 changes
->args
[used
++].u
.member
= mn
;
4225 if(mn
->modes
& MODE_VOICE
)
4227 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4228 changes
->args
[used
++].u
.member
= mn
;
4231 else if(!cData
->lvlOpts
[lvlGiveVoice
]
4232 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
]))
4234 if(mn
->modes
& MODE_CHANOP
)
4236 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4237 changes
->args
[used
++].u
.member
= mn
;
4239 if(mn
->modes
& MODE_HALFOP
)
4241 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4242 changes
->args
[used
++].u
.member
= mn
;
4244 if(!(mn
->modes
& MODE_VOICE
))
4246 changes
->args
[used
].mode
= MODE_VOICE
;
4247 changes
->args
[used
++].u
.member
= mn
;
4254 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4255 changes
->args
[used
++].u
.member
= mn
;
4259 changes
->argc
= used
;
4260 modcmd_chanmode_announce(changes
);
4261 mod_chanmode_free(changes
);
4262 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4266 static CHANSERV_FUNC(cmd_seen
)
4268 struct userData
*uData
;
4269 struct handle_info
*handle
;
4270 char seen
[INTERVALLEN
];
4274 if(!irccasecmp(argv
[1], chanserv
->nick
))
4276 reply("CSMSG_IS_CHANSERV");
4280 if(!(handle
= get_handle_info(argv
[1])))
4282 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4286 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4288 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4293 reply("CSMSG_USER_PRESENT", handle
->handle
);
4294 else if(uData
->seen
)
4295 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4297 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4299 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4300 reply("CSMSG_USER_VACATION", handle
->handle
);
4305 static MODCMD_FUNC(cmd_names
)
4307 struct userNode
*targ
;
4308 struct userData
*targData
;
4309 unsigned int ii
, pos
;
4312 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4314 targ
= channel
->members
.list
[ii
]->user
;
4315 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4318 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4321 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4325 if(IsUserSuspended(targData
))
4327 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4330 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4331 reply("CSMSG_END_NAMES", channel
->name
);
4336 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4338 switch(ntype
->visible_type
)
4340 case NOTE_VIS_ALL
: return 1;
4341 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4342 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4347 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4349 struct userData
*uData
;
4351 switch(ntype
->set_access_type
)
4353 case NOTE_SET_CHANNEL_ACCESS
:
4354 if(!user
->handle_info
)
4356 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4358 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4359 case NOTE_SET_CHANNEL_SETTER
:
4360 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4361 case NOTE_SET_PRIVILEGED
: default:
4362 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4366 static CHANSERV_FUNC(cmd_note
)
4368 struct chanData
*cData
;
4370 struct note_type
*ntype
;
4372 cData
= channel
->channel_info
;
4375 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4379 /* If no arguments, show all visible notes for the channel. */
4385 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4387 note
= iter_data(it
);
4388 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4391 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4392 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4395 reply("CSMSG_NOTELIST_END", channel
->name
);
4397 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4399 /* If one argument, show the named note. */
4402 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4403 && note_type_visible_to_user(cData
, note
->type
, user
))
4405 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4407 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4408 && note_type_visible_to_user(NULL
, ntype
, user
))
4410 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4415 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4419 /* Assume they're trying to set a note. */
4423 ntype
= dict_find(note_types
, argv
[1], NULL
);
4426 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4429 else if(note_type_settable_by_user(channel
, ntype
, user
))
4431 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4432 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4433 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4434 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4435 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4437 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4439 /* The note is viewable to staff only, so return 0
4440 to keep the invocation from getting logged (or
4441 regular users can see it in !events). */
4447 reply("CSMSG_NO_ACCESS");
4454 static CHANSERV_FUNC(cmd_delnote
)
4459 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4460 || !note_type_settable_by_user(channel
, note
->type
, user
))
4462 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4465 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4466 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4470 static CHANSERV_FUNC(cmd_events
)
4472 struct logSearch discrim
;
4473 struct logReport report
;
4474 unsigned int matches
, limit
;
4476 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4477 if(limit
< 1 || limit
> 200)
4480 memset(&discrim
, 0, sizeof(discrim
));
4481 discrim
.masks
.bot
= chanserv
;
4482 discrim
.masks
.channel_name
= channel
->name
;
4484 discrim
.masks
.command
= argv
[2];
4485 discrim
.limit
= limit
;
4486 discrim
.max_time
= INT_MAX
;
4487 discrim
.severities
= 1 << LOG_COMMAND
;
4488 report
.reporter
= chanserv
;
4490 reply("CSMSG_EVENT_SEARCH_RESULTS");
4491 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4493 reply("MSG_MATCH_COUNT", matches
);
4495 reply("MSG_NO_MATCHES");
4499 static CHANSERV_FUNC(cmd_say
)
4505 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4506 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4508 else if(GetUserH(argv
[1]))
4511 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4512 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4516 reply("MSG_NOT_TARGET_NAME");
4522 static CHANSERV_FUNC(cmd_emote
)
4528 /* CTCP is so annoying. */
4529 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4530 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4532 else if(GetUserH(argv
[1]))
4534 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4535 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4539 reply("MSG_NOT_TARGET_NAME");
4545 struct channelList
*
4546 chanserv_support_channels(void)
4548 return &chanserv_conf
.support_channels
;
4551 static CHANSERV_FUNC(cmd_expire
)
4553 int channel_count
= registered_channels
;
4554 expire_channels(NULL
);
4555 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4560 chanserv_expire_suspension(void *data
)
4562 struct suspended
*suspended
= data
;
4563 struct chanNode
*channel
;
4565 if(!suspended
->expires
|| (now
< suspended
->expires
))
4566 suspended
->revoked
= now
;
4567 channel
= suspended
->cData
->channel
;
4568 suspended
->cData
->channel
= channel
;
4569 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4570 if(!IsOffChannel(suspended
->cData
))
4572 struct mod_chanmode change
;
4573 mod_chanmode_init(&change
);
4575 change
.args
[0].mode
= MODE_CHANOP
;
4576 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4577 mod_chanmode_announce(chanserv
, channel
, &change
);
4581 static CHANSERV_FUNC(cmd_csuspend
)
4583 struct suspended
*suspended
;
4584 char reason
[MAXLEN
];
4585 time_t expiry
, duration
;
4586 struct userData
*uData
;
4590 if(IsProtected(channel
->channel_info
))
4592 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4596 if(argv
[1][0] == '!')
4598 else if(IsSuspended(channel
->channel_info
))
4600 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4601 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4605 if(!strcmp(argv
[1], "0"))
4607 else if((duration
= ParseInterval(argv
[1])))
4608 expiry
= now
+ duration
;
4611 reply("MSG_INVALID_DURATION", argv
[1]);
4615 unsplit_string(argv
+ 2, argc
- 2, reason
);
4617 suspended
= calloc(1, sizeof(*suspended
));
4618 suspended
->revoked
= 0;
4619 suspended
->issued
= now
;
4620 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4621 suspended
->expires
= expiry
;
4622 suspended
->reason
= strdup(reason
);
4623 suspended
->cData
= channel
->channel_info
;
4624 suspended
->previous
= suspended
->cData
->suspended
;
4625 suspended
->cData
->suspended
= suspended
;
4627 if(suspended
->expires
)
4628 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4630 if(IsSuspended(channel
->channel_info
))
4632 suspended
->previous
->revoked
= now
;
4633 if(suspended
->previous
->expires
)
4634 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4635 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4636 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4640 /* Mark all users in channel as absent. */
4641 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4650 /* Mark the channel as suspended, then part. */
4651 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4652 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4653 reply("CSMSG_SUSPENDED", channel
->name
);
4654 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4655 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4660 static CHANSERV_FUNC(cmd_cunsuspend
)
4662 struct suspended
*suspended
;
4663 char message
[MAXLEN
];
4665 if(!IsSuspended(channel
->channel_info
))
4667 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4671 suspended
= channel
->channel_info
->suspended
;
4673 /* Expire the suspension and join ChanServ to the channel. */
4674 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4675 chanserv_expire_suspension(suspended
);
4676 reply("CSMSG_UNSUSPENDED", channel
->name
);
4677 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4678 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4682 typedef struct chanservSearch
4690 unsigned long flags
;
4694 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4697 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4702 search
= malloc(sizeof(struct chanservSearch
));
4703 memset(search
, 0, sizeof(*search
));
4706 for(i
= 0; i
< argc
; i
++)
4708 /* Assume all criteria require arguments. */
4711 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4715 if(!irccasecmp(argv
[i
], "name"))
4716 search
->name
= argv
[++i
];
4717 else if(!irccasecmp(argv
[i
], "registrar"))
4718 search
->registrar
= argv
[++i
];
4719 else if(!irccasecmp(argv
[i
], "unvisited"))
4720 search
->unvisited
= ParseInterval(argv
[++i
]);
4721 else if(!irccasecmp(argv
[i
], "registered"))
4722 search
->registered
= ParseInterval(argv
[++i
]);
4723 else if(!irccasecmp(argv
[i
], "flags"))
4726 if(!irccasecmp(argv
[i
], "nodelete"))
4727 search
->flags
|= CHANNEL_NODELETE
;
4728 else if(!irccasecmp(argv
[i
], "suspended"))
4729 search
->flags
|= CHANNEL_SUSPENDED
;
4732 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4736 else if(!irccasecmp(argv
[i
], "limit"))
4737 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4740 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4745 if(search
->name
&& !strcmp(search
->name
, "*"))
4747 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4748 search
->registrar
= 0;
4757 chanserv_channel_match(struct chanData
*channel
, search_t search
)
4759 const char *name
= channel
->channel
->name
;
4760 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
4761 (search
->registrar
&& !channel
->registrar
) ||
4762 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
4763 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
4764 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
4765 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
4772 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
4774 struct chanData
*channel
;
4775 unsigned int matches
= 0;
4777 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
4779 if(!chanserv_channel_match(channel
, search
))
4789 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
4794 search_print(struct chanData
*channel
, void *data
)
4796 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
4799 static CHANSERV_FUNC(cmd_search
)
4802 unsigned int matches
;
4803 channel_search_func action
;
4807 if(!irccasecmp(argv
[1], "count"))
4808 action
= search_count
;
4809 else if(!irccasecmp(argv
[1], "print"))
4810 action
= search_print
;
4813 reply("CSMSG_ACTION_INVALID", argv
[1]);
4817 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
4821 if(action
== search_count
)
4822 search
->limit
= INT_MAX
;
4824 if(action
== search_print
)
4825 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4827 matches
= chanserv_channel_search(search
, action
, user
);
4830 reply("MSG_MATCH_COUNT", matches
);
4832 reply("MSG_NO_MATCHES");
4838 static CHANSERV_FUNC(cmd_unvisited
)
4840 struct chanData
*cData
;
4841 time_t interval
= chanserv_conf
.channel_expire_delay
;
4842 char buffer
[INTERVALLEN
];
4843 unsigned int limit
= 25, matches
= 0;
4847 interval
= ParseInterval(argv
[1]);
4849 limit
= atoi(argv
[2]);
4852 intervalString(buffer
, interval
, user
->handle_info
);
4853 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
4855 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
4857 if((now
- cData
->visited
) < interval
)
4860 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
4861 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
4868 static MODCMD_FUNC(chan_opt_defaulttopic
)
4874 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4876 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4880 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
4882 free(channel
->channel_info
->topic
);
4883 if(topic
[0] == '*' && topic
[1] == 0)
4885 topic
= channel
->channel_info
->topic
= NULL
;
4889 topic
= channel
->channel_info
->topic
= strdup(topic
);
4890 if(channel
->channel_info
->topic_mask
4891 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
4892 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4894 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
4897 if(channel
->channel_info
->topic
)
4898 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
4900 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
4904 static MODCMD_FUNC(chan_opt_topicmask
)
4908 struct chanData
*cData
= channel
->channel_info
;
4911 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4913 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4917 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
4919 if(cData
->topic_mask
)
4920 free(cData
->topic_mask
);
4921 if(mask
[0] == '*' && mask
[1] == 0)
4923 cData
->topic_mask
= 0;
4927 cData
->topic_mask
= strdup(mask
);
4929 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
4930 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
4931 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4935 if(channel
->channel_info
->topic_mask
)
4936 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
4938 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
4942 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
4946 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
4950 if(greeting
[0] == '*' && greeting
[1] == 0)
4954 unsigned int length
= strlen(greeting
);
4955 if(length
> chanserv_conf
.greeting_length
)
4957 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
4960 *data
= strdup(greeting
);
4969 reply(name
, user_find_message(user
, "MSG_NONE"));
4973 static MODCMD_FUNC(chan_opt_greeting
)
4975 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
4978 static MODCMD_FUNC(chan_opt_usergreeting
)
4980 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
4983 static MODCMD_FUNC(chan_opt_modes
)
4985 struct mod_chanmode
*new_modes
;
4986 char modes
[MODELEN
];
4990 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
4992 reply("CSMSG_NO_ACCESS");
4995 if(argv
[1][0] == '*' && argv
[1][1] == 0)
4997 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
4999 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5001 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5004 else if(new_modes
->argc
> 1)
5006 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5007 mod_chanmode_free(new_modes
);
5012 channel
->channel_info
->modes
= *new_modes
;
5013 modcmd_chanmode_announce(new_modes
);
5014 mod_chanmode_free(new_modes
);
5018 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5020 reply("CSMSG_SET_MODES", modes
);
5022 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5026 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5028 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5030 struct chanData
*cData
= channel
->channel_info
;
5035 /* Set flag according to value. */
5036 if(enabled_string(argv
[1]))
5038 cData
->flags
|= mask
;
5041 else if(disabled_string(argv
[1]))
5043 cData
->flags
&= ~mask
;
5048 reply("MSG_INVALID_BINARY", argv
[1]);
5054 /* Find current option value. */
5055 value
= (cData
->flags
& mask
) ? 1 : 0;
5059 reply(name
, user_find_message(user
, "MSG_ON"));
5061 reply(name
, user_find_message(user
, "MSG_OFF"));
5065 static MODCMD_FUNC(chan_opt_nodelete
)
5067 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5069 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5073 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5076 static MODCMD_FUNC(chan_opt_dynlimit
)
5078 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5081 static MODCMD_FUNC(chan_opt_offchannel
)
5083 struct chanData
*cData
= channel
->channel_info
;
5088 /* Set flag according to value. */
5089 if(enabled_string(argv
[1]))
5091 if(!IsOffChannel(cData
))
5092 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5093 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5096 else if(disabled_string(argv
[1]))
5098 if(IsOffChannel(cData
))
5100 struct mod_chanmode change
;
5101 mod_chanmode_init(&change
);
5103 change
.args
[0].mode
= MODE_CHANOP
;
5104 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5105 mod_chanmode_announce(chanserv
, channel
, &change
);
5107 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5112 reply("MSG_INVALID_BINARY", argv
[1]);
5118 /* Find current option value. */
5119 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5123 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5125 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5129 static MODCMD_FUNC(chan_opt_defaults
)
5131 struct userData
*uData
;
5132 struct chanData
*cData
;
5133 const char *confirm
;
5134 enum levelOption lvlOpt
;
5135 enum charOption chOpt
;
5137 cData
= channel
->channel_info
;
5138 uData
= GetChannelUser(cData
, user
->handle_info
);
5139 if(!uData
|| (uData
->access
< UL_OWNER
))
5141 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5144 confirm
= make_confirmation_string(uData
);
5145 if((argc
< 2) || strcmp(argv
[1], confirm
))
5147 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5150 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5151 cData
->modes
= chanserv_conf
.default_modes
;
5152 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5153 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5154 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5155 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5156 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5161 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5163 struct chanData
*cData
= channel
->channel_info
;
5164 struct userData
*uData
;
5165 unsigned short value
;
5169 if(!check_user_level(channel
, user
, option
, 1, 1))
5171 reply("CSMSG_CANNOT_SET");
5174 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5175 if(!value
&& strcmp(argv
[1], "0"))
5177 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5180 uData
= GetChannelUser(cData
, user
->handle_info
);
5181 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5183 reply("CSMSG_BAD_SETLEVEL");
5189 if(value
> cData
->lvlOpts
[lvlGiveOps
])
5191 reply("CSMSG_BAD_GIVEVOICE", cData
->lvlOpts
[lvlGiveOps
]);
5195 case lvlGiveHalfOps
:
5196 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5198 reply("CSMSG_BAD_GIVEHOPS", cData
->lvlOpts
[lvlGiveHalfOps
]);
5203 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5205 reply("CSMSG_BAD_GIVEOPS", cData
->lvlOpts
[lvlGiveVoice
]);
5210 /* This test only applies to owners, since non-owners
5211 * trying to set an option to above their level get caught
5212 * by the CSMSG_BAD_SETLEVEL test above.
5214 if(value
> uData
->access
)
5216 reply("CSMSG_BAD_SETTERS");
5223 cData
->lvlOpts
[option
] = value
;
5225 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5229 static MODCMD_FUNC(chan_opt_enfops
)
5231 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5234 static MODCMD_FUNC(chan_opt_enfhalfops
)
5236 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5239 static MODCMD_FUNC(chan_opt_giveops
)
5241 return channel_level_option(lvlGiveOps
, CSFUNC_ARGS
);
5244 static MODCMD_FUNC(chan_opt_givehalfops
)
5246 return channel_level_option(lvlGiveHalfOps
, CSFUNC_ARGS
);
5249 static MODCMD_FUNC(chan_opt_enfmodes
)
5251 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5254 static MODCMD_FUNC(chan_opt_enftopic
)
5256 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5259 static MODCMD_FUNC(chan_opt_pubcmd
)
5261 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5264 static MODCMD_FUNC(chan_opt_setters
)
5266 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5269 static MODCMD_FUNC(chan_opt_ctcpusers
)
5271 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5274 static MODCMD_FUNC(chan_opt_userinfo
)
5276 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5279 static MODCMD_FUNC(chan_opt_givevoice
)
5281 return channel_level_option(lvlGiveVoice
, CSFUNC_ARGS
);
5284 static MODCMD_FUNC(chan_opt_topicsnarf
)
5286 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5289 static MODCMD_FUNC(chan_opt_inviteme
)
5291 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5295 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5297 struct chanData
*cData
= channel
->channel_info
;
5298 int count
= charOptions
[option
].count
, index
;
5302 index
= atoi(argv
[1]);
5304 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5306 reply("CSMSG_INVALID_NUMERIC", index
);
5307 /* Show possible values. */
5308 for(index
= 0; index
< count
; index
++)
5309 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5313 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5317 /* Find current option value. */
5320 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5324 /* Somehow, the option value is corrupt; reset it to the default. */
5325 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5330 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5334 static MODCMD_FUNC(chan_opt_protect
)
5336 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5339 static MODCMD_FUNC(chan_opt_toys
)
5341 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5344 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5346 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5349 static MODCMD_FUNC(chan_opt_topicrefresh
)
5351 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5354 static struct svccmd_list set_shows_list
;
5357 handle_svccmd_unbind(struct svccmd
*target
) {
5359 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5360 if(target
== set_shows_list
.list
[ii
])
5361 set_shows_list
.used
= 0;
5364 static CHANSERV_FUNC(cmd_set
)
5366 struct svccmd
*subcmd
;
5370 /* Check if we need to (re-)initialize set_shows_list. */
5371 if(!set_shows_list
.used
)
5373 if(!set_shows_list
.size
)
5375 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5376 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5378 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5380 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5381 sprintf(buf
, "%s %s", argv
[0], name
);
5382 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5385 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5388 svccmd_list_append(&set_shows_list
, subcmd
);
5394 reply("CSMSG_CHANNEL_OPTIONS");
5395 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5397 subcmd
= set_shows_list
.list
[ii
];
5398 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5403 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5404 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5407 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5410 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5412 reply("CSMSG_NO_ACCESS");
5416 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5420 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5422 struct userData
*uData
;
5424 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5427 reply("CSMSG_NOT_USER", channel
->name
);
5433 /* Just show current option value. */
5435 else if(enabled_string(argv
[1]))
5437 uData
->flags
|= mask
;
5439 else if(disabled_string(argv
[1]))
5441 uData
->flags
&= ~mask
;
5445 reply("MSG_INVALID_BINARY", argv
[1]);
5449 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5453 static MODCMD_FUNC(user_opt_noautoop
)
5455 struct userData
*uData
;
5457 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5460 reply("CSMSG_NOT_USER", channel
->name
);
5463 if(uData
->access
< channel
->channel_info
->lvlOpts
[lvlGiveOps
])
5464 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5466 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5469 static MODCMD_FUNC(user_opt_autoinvite
)
5471 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5474 static MODCMD_FUNC(user_opt_info
)
5476 struct userData
*uData
;
5479 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5483 /* If they got past the command restrictions (which require access)
5484 * but fail this test, we have some fool with security override on.
5486 reply("CSMSG_NOT_USER", channel
->name
);
5493 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5494 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5496 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5499 bp
= strcspn(infoline
, "\001");
5502 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5507 if(infoline
[0] == '*' && infoline
[1] == 0)
5510 uData
->info
= strdup(infoline
);
5513 reply("CSMSG_USET_INFO", uData
->info
);
5515 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5519 struct svccmd_list uset_shows_list
;
5521 static CHANSERV_FUNC(cmd_uset
)
5523 struct svccmd
*subcmd
;
5527 /* Check if we need to (re-)initialize uset_shows_list. */
5528 if(!uset_shows_list
.used
)
5532 "NoAutoOp", "AutoInvite", "Info"
5535 if(!uset_shows_list
.size
)
5537 uset_shows_list
.size
= ArrayLength(options
);
5538 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5540 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5542 const char *name
= options
[ii
];
5543 sprintf(buf
, "%s %s", argv
[0], name
);
5544 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5547 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5550 svccmd_list_append(&uset_shows_list
, subcmd
);
5556 /* Do this so options are presented in a consistent order. */
5557 reply("CSMSG_USER_OPTIONS");
5558 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5559 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5563 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5564 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5567 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5571 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5574 static CHANSERV_FUNC(cmd_giveownership
)
5576 struct handle_info
*new_owner_hi
;
5577 struct userData
*new_owner
, *curr_user
;
5578 struct chanData
*cData
= channel
->channel_info
;
5579 struct do_not_register
*dnr
;
5581 unsigned short co_access
;
5582 char reason
[MAXLEN
];
5585 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5586 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5587 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5589 struct userData
*owner
= NULL
;
5590 for(curr_user
= channel
->channel_info
->users
;
5592 curr_user
= curr_user
->next
)
5594 if(curr_user
->access
!= UL_OWNER
)
5598 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5605 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5607 char delay
[INTERVALLEN
];
5608 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5609 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5612 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5614 if(new_owner_hi
== user
->handle_info
)
5616 reply("CSMSG_NO_TRANSFER_SELF");
5619 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5624 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5628 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5632 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5634 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5637 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5638 if(!IsHelping(user
))
5639 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5641 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5644 if(new_owner
->access
>= UL_COOWNER
)
5645 co_access
= new_owner
->access
;
5647 co_access
= UL_COOWNER
;
5648 new_owner
->access
= UL_OWNER
;
5650 curr_user
->access
= co_access
;
5651 cData
->ownerTransfer
= now
;
5652 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5653 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5654 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5658 static CHANSERV_FUNC(cmd_suspend
)
5660 struct handle_info
*hi
;
5661 struct userData
*self
, *target
;
5664 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5665 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5666 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5668 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5671 if(target
->access
>= self
->access
)
5673 reply("MSG_USER_OUTRANKED", hi
->handle
);
5676 if(target
->flags
& USER_SUSPENDED
)
5678 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5683 target
->present
= 0;
5686 target
->flags
|= USER_SUSPENDED
;
5687 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5691 static CHANSERV_FUNC(cmd_unsuspend
)
5693 struct handle_info
*hi
;
5694 struct userData
*self
, *target
;
5697 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5698 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5699 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5701 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5704 if(target
->access
>= self
->access
)
5706 reply("MSG_USER_OUTRANKED", hi
->handle
);
5709 if(!(target
->flags
& USER_SUSPENDED
))
5711 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5714 target
->flags
&= ~USER_SUSPENDED
;
5715 scan_user_presence(target
, NULL
);
5716 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5720 static MODCMD_FUNC(cmd_deleteme
)
5722 struct handle_info
*hi
;
5723 struct userData
*target
;
5724 const char *confirm_string
;
5725 unsigned short access
;
5728 hi
= user
->handle_info
;
5729 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5731 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5734 if(target
->access
== UL_OWNER
)
5736 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5739 confirm_string
= make_confirmation_string(target
);
5740 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
5742 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
5745 access
= target
->access
;
5746 channel_name
= strdup(channel
->name
);
5747 del_channel_user(target
, 1);
5748 reply("CSMSG_DELETED_YOU", access
, channel_name
);
5754 chanserv_refresh_topics(UNUSED_ARG(void *data
))
5756 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
5757 struct chanData
*cData
;
5760 for(cData
= channelList
; cData
; cData
= cData
->next
)
5762 if(IsSuspended(cData
))
5764 opt
= cData
->chOpts
[chTopicRefresh
];
5767 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
5770 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
5771 cData
->last_refresh
= refresh_num
;
5773 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
5776 static CHANSERV_FUNC(cmd_unf
)
5780 char response
[MAXLEN
];
5781 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
5782 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5783 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5786 reply("CSMSG_UNF_RESPONSE");
5790 static CHANSERV_FUNC(cmd_ping
)
5794 char response
[MAXLEN
];
5795 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
5796 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5797 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5800 reply("CSMSG_PING_RESPONSE");
5804 static CHANSERV_FUNC(cmd_wut
)
5808 char response
[MAXLEN
];
5809 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
5810 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5811 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5814 reply("CSMSG_WUT_RESPONSE");
5818 static CHANSERV_FUNC(cmd_8ball
)
5820 unsigned int i
, j
, accum
;
5825 for(i
=1; i
<argc
; i
++)
5826 for(j
=0; argv
[i
][j
]; j
++)
5827 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
5828 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
5831 char response
[MAXLEN
];
5832 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
5833 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5836 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
5840 static CHANSERV_FUNC(cmd_d
)
5842 unsigned long sides
, count
, modifier
, ii
, total
;
5843 char response
[MAXLEN
], *sep
;
5847 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
5857 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
5858 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
5862 else if((sep
[0] == '-') && isdigit(sep
[1]))
5863 modifier
= strtoul(sep
, NULL
, 10);
5864 else if((sep
[0] == '+') && isdigit(sep
[1]))
5865 modifier
= strtoul(sep
+1, NULL
, 10);
5872 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
5877 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
5880 for(total
= ii
= 0; ii
< count
; ++ii
)
5881 total
+= (rand() % sides
) + 1;
5884 if((count
> 1) || modifier
)
5886 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
5887 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
5891 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
5892 sprintf(response
, fmt
, total
, sides
);
5895 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5897 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5901 static CHANSERV_FUNC(cmd_huggle
)
5903 /* CTCP must be via PRIVMSG, never notice */
5905 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
5907 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
5911 static CHANSERV_FUNC(cmd_calc
)
5913 char response
[MAXLEN
];
5916 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
5919 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5921 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5926 chanserv_adjust_limit(void *data
)
5928 struct mod_chanmode change
;
5929 struct chanData
*cData
= data
;
5930 struct chanNode
*channel
= cData
->channel
;
5933 if(IsSuspended(cData
))
5936 cData
->limitAdjusted
= now
;
5937 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
5938 if(cData
->modes
.modes_set
& MODE_LIMIT
)
5940 if(limit
> cData
->modes
.new_limit
)
5941 limit
= cData
->modes
.new_limit
;
5942 else if(limit
== cData
->modes
.new_limit
)
5946 mod_chanmode_init(&change
);
5947 change
.modes_set
= MODE_LIMIT
;
5948 change
.new_limit
= limit
;
5949 mod_chanmode_announce(chanserv
, channel
, &change
);
5953 handle_new_channel(struct chanNode
*channel
)
5955 struct chanData
*cData
;
5957 if(!(cData
= channel
->channel_info
))
5960 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
5961 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
5963 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
5964 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
5967 /* Welcome to my worst nightmare. Warning: Read (or modify)
5968 the code below at your own risk. */
5970 handle_join(struct modeNode
*mNode
)
5972 struct mod_chanmode change
;
5973 struct userNode
*user
= mNode
->user
;
5974 struct chanNode
*channel
= mNode
->channel
;
5975 struct chanData
*cData
;
5976 struct userData
*uData
= NULL
;
5977 struct banData
*bData
;
5978 struct handle_info
*handle
;
5979 unsigned int modes
= 0, info
= 0;
5982 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
5985 cData
= channel
->channel_info
;
5986 if(channel
->members
.used
> cData
->max
)
5987 cData
->max
= channel
->members
.used
;
5989 /* Check for bans. If they're joining through a ban, one of two
5991 * 1: Join during a netburst, by riding the break. Kick them
5992 * unless they have ops or voice in the channel.
5993 * 2: They're allowed to join through the ban (an invite in
5994 * ircu2.10, or a +e on Hybrid, or something).
5995 * If they're not joining through a ban, and the banlist is not
5996 * full, see if they're on the banlist for the channel. If so,
5999 /* This is really, really stupid. not all banned people are kicked.
6000 * sometimes we like to leave them unkicked.
6001 * I tried to explain this to the srvx developers and
6002 * got insulted.. hence one reason for this fork.
6004 if(user->uplink->burst && !mNode->modes)
6007 for(ii = 0; ii < channel->banlist.used; ii++)
6009 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6011 ** Riding a netburst. Naughty. **
6012 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6019 mod_chanmode_init(&change
);
6021 if(channel
->banlist
.used
< MAXBANS
)
6023 /* Not joining through a ban. */
6024 for(bData
= cData
->bans
;
6025 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6026 bData
= bData
->next
);
6030 char kick_reason
[MAXLEN
];
6031 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6033 bData
->triggered
= now
;
6034 if(bData
!= cData
->bans
)
6036 /* Shuffle the ban to the head of the list. */
6038 bData
->next
->prev
= bData
->prev
;
6040 bData
->prev
->next
= bData
->next
;
6043 bData
->next
= cData
->bans
;
6046 cData
->bans
->prev
= bData
;
6047 cData
->bans
= bData
;
6050 change
.args
[0].mode
= MODE_BAN
;
6051 change
.args
[0].u
.hostmask
= bData
->mask
;
6052 mod_chanmode_announce(chanserv
, channel
, &change
);
6053 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6058 /* ChanServ will not modify the limits in join-flooded channels.
6059 It will also skip DynLimit processing when the user (or srvx)
6060 is bursting in, because there are likely more incoming. */
6061 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6062 && !user
->uplink
->burst
6063 && !channel
->join_flooded
6064 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6066 /* The user count has begun "bumping" into the channel limit,
6067 so set a timer to raise the limit a bit. Any previous
6068 timers are removed so three incoming users within the delay
6069 results in one limit change, not three. */
6071 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6072 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6075 if(channel
->join_flooded
)
6077 /* don't automatically give ops or voice during a join flood */
6079 else if(cData
->lvlOpts
[lvlGiveOps
] == 0)
6080 modes
|= MODE_CHANOP
;
6081 else if(cData
->lvlOpts
[lvlGiveHalfOps
] == 0)
6082 modes
|= MODE_HALFOP
;
6083 else if(cData
->lvlOpts
[lvlGiveVoice
] == 0)
6084 modes
|= MODE_VOICE
;
6086 greeting
= cData
->greeting
;
6087 if(user
->handle_info
)
6089 handle
= user
->handle_info
;
6091 if(IsHelper(user
) && !IsHelping(user
))
6094 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6096 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6098 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6104 uData
= GetTrueChannelAccess(cData
, handle
);
6105 if(uData
&& !IsUserSuspended(uData
))
6107 /* Ops and above were handled by the above case. */
6108 if(IsUserAutoOp(uData
))
6110 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
6111 modes
|= MODE_CHANOP
;
6112 if(uData
->access
>= cData
->lvlOpts
[lvlGiveHalfOps
])
6113 modes
|= MODE_HALFOP
;
6114 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
6115 modes
|= MODE_VOICE
;
6117 if(uData
->access
>= UL_PRESENT
)
6118 cData
->visited
= now
;
6119 if(cData
->user_greeting
)
6120 greeting
= cData
->user_greeting
;
6122 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6123 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6130 if(!user
->uplink
->burst
)
6134 if(modes
& MODE_CHANOP
) {
6135 modes
&= ~MODE_HALFOP
;
6136 modes
&= ~MODE_VOICE
;
6138 change
.args
[0].mode
= modes
;
6139 change
.args
[0].u
.member
= mNode
;
6140 mod_chanmode_announce(chanserv
, channel
, &change
);
6142 if(greeting
&& !user
->uplink
->burst
)
6143 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6145 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6151 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6153 struct mod_chanmode change
;
6154 struct userData
*channel
;
6155 unsigned int ii
, jj
;
6157 if(!user
->handle_info
)
6160 mod_chanmode_init(&change
);
6162 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6164 struct chanNode
*cn
;
6165 struct modeNode
*mn
;
6166 if(IsUserSuspended(channel
)
6167 || IsSuspended(channel
->channel
)
6168 || !(cn
= channel
->channel
->channel
))
6171 mn
= GetUserMode(cn
, user
);
6174 if(!IsUserSuspended(channel
)
6175 && IsUserAutoInvite(channel
)
6176 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6178 && !user
->uplink
->burst
)
6179 irc_invite(chanserv
, user
, cn
);
6183 if(channel
->access
>= UL_PRESENT
)
6184 channel
->channel
->visited
= now
;
6186 if(IsUserAutoOp(channel
))
6188 if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveOps
])
6189 change
.args
[0].mode
= MODE_CHANOP
;
6190 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveHalfOps
])
6191 change
.args
[0].mode
= MODE_HALFOP
;
6192 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveVoice
])
6193 change
.args
[0].mode
= MODE_VOICE
;
6195 change
.args
[0].mode
= 0;
6196 change
.args
[0].u
.member
= mn
;
6197 if(change
.args
[0].mode
)
6198 mod_chanmode_announce(chanserv
, cn
, &change
);
6201 channel
->seen
= now
;
6202 channel
->present
= 1;
6205 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6207 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6208 struct banData
*ban
;
6210 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6211 || !channel
->channel_info
6212 || IsSuspended(channel
->channel_info
))
6214 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6215 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6217 if(jj
< channel
->banlist
.used
)
6219 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6221 char kick_reason
[MAXLEN
];
6222 if(!user_matches_glob(user
, ban
->mask
, 1))
6224 change
.args
[0].mode
= MODE_BAN
;
6225 change
.args
[0].u
.hostmask
= ban
->mask
;
6226 mod_chanmode_announce(chanserv
, channel
, &change
);
6227 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6228 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6229 ban
->triggered
= now
;
6234 if(IsSupportHelper(user
))
6236 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6238 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6240 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6248 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6250 struct chanData
*cData
;
6251 struct userData
*uData
;
6253 cData
= mn
->channel
->channel_info
;
6254 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6257 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6259 /* Allow for a bit of padding so that the limit doesn't
6260 track the user count exactly, which could get annoying. */
6261 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6263 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6264 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6268 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6270 scan_user_presence(uData
, mn
->user
);
6274 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6276 unsigned int ii
, jj
;
6277 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6279 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6280 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6282 if(jj
< mn
->user
->channels
.used
)
6285 if(ii
== chanserv_conf
.support_channels
.used
)
6286 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6291 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6293 struct userData
*uData
;
6295 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6296 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6297 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6300 if(protect_user(victim
, kicker
, channel
->channel_info
))
6302 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6303 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6306 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6311 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6313 struct chanData
*cData
;
6315 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6318 cData
= channel
->channel_info
;
6319 if(bad_topic(channel
, user
, channel
->topic
))
6321 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6322 if(cData
->topic_mask
&& match_ircglob(old_topic
, cData
->topic_mask
))
6323 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6324 else if(cData
->topic
)
6325 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
6328 /* With topicsnarf, grab the topic and save it as the default topic. */
6329 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6332 cData
->topic
= strdup(channel
->topic
);
6338 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6340 struct mod_chanmode
*bounce
= NULL
;
6341 unsigned int bnc
, ii
;
6344 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6347 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6348 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6350 char correct
[MAXLEN
];
6351 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6352 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6353 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6355 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6357 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6359 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6360 if(!protect_user(victim
, user
, channel
->channel_info
))
6363 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6366 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6367 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6368 if(bounce
->args
[bnc
].u
.member
)
6372 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6373 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6375 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6377 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6379 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6380 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6383 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6384 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6385 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6388 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6390 const char *ban
= change
->args
[ii
].u
.hostmask
;
6391 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6394 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6395 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6396 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6398 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6403 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6404 mod_chanmode_announce(chanserv
, channel
, bounce
);
6405 for(ii
= 0; ii
< change
->argc
; ++ii
)
6406 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6407 free((char*)bounce
->args
[ii
].u
.hostmask
);
6408 mod_chanmode_free(bounce
);
6413 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6415 struct chanNode
*channel
;
6416 struct banData
*bData
;
6417 struct mod_chanmode change
;
6418 unsigned int ii
, jj
;
6419 char kick_reason
[MAXLEN
];
6421 mod_chanmode_init(&change
);
6423 change
.args
[0].mode
= MODE_BAN
;
6424 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6426 channel
= user
->channels
.list
[ii
]->channel
;
6427 /* Need not check for bans if they're opped or voiced. */
6428 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6430 /* Need not check for bans unless channel registration is active. */
6431 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6433 /* Look for a matching ban already on the channel. */
6434 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6435 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6437 /* Need not act if we found one. */
6438 if(jj
< channel
->banlist
.used
)
6440 /* Look for a matching ban in this channel. */
6441 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6443 if(!user_matches_glob(user
, bData
->mask
, 1))
6445 change
.args
[0].u
.hostmask
= bData
->mask
;
6446 mod_chanmode_announce(chanserv
, channel
, &change
);
6447 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6448 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6449 bData
->triggered
= now
;
6450 break; /* we don't need to check any more bans in the channel */
6455 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6457 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6461 dict_remove2(handle_dnrs
, old_handle
, 1);
6462 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6463 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6468 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6470 struct userNode
*h_user
;
6472 if(handle
->channels
)
6474 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6475 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6477 while(handle
->channels
)
6478 del_channel_user(handle
->channels
, 1);
6483 handle_server_link(UNUSED_ARG(struct server
*server
))
6485 struct chanData
*cData
;
6487 for(cData
= channelList
; cData
; cData
= cData
->next
)
6489 if(!IsSuspended(cData
))
6490 cData
->may_opchan
= 1;
6491 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6492 && !cData
->channel
->join_flooded
6493 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6494 < chanserv_conf
.adjust_threshold
))
6496 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6497 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6503 chanserv_conf_read(void)
6507 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6508 struct mod_chanmode
*change
;
6509 struct string_list
*strlist
;
6510 struct chanNode
*chan
;
6513 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6515 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6518 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6519 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6520 chanserv_conf
.support_channels
.used
= 0;
6521 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6523 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6525 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6528 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6530 channelList_append(&chanserv_conf
.support_channels
, chan
);
6533 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6536 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6539 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6541 channelList_append(&chanserv_conf
.support_channels
, chan
);
6543 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6544 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6545 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6546 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6547 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6548 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6549 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6550 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6551 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6552 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6553 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6554 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6555 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6556 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6557 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6558 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6559 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6560 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6561 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6562 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6563 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6564 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6565 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6567 NickChange(chanserv
, str
, 0);
6568 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6569 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6570 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6571 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6572 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6573 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6574 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6575 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6576 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6577 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6578 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6579 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6580 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6581 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6582 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6583 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6584 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6587 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6588 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6589 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6591 chanserv_conf
.default_modes
= *change
;
6592 mod_chanmode_free(change
);
6594 free_string_list(chanserv_conf
.set_shows
);
6595 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6597 strlist
= string_list_copy(strlist
);
6600 static const char *list
[] = {
6601 /* free form text */
6602 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6603 /* options based on user level */
6604 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveHalfOps", "GiveOps", "EnfOps",
6605 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6606 /* multiple choice options */
6607 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6608 /* binary options */
6609 "DynLimit", "NoDelete",
6614 strlist
= alloc_string_list(ArrayLength(list
)-1);
6615 for(ii
=0; list
[ii
]; ii
++)
6616 string_list_append(strlist
, strdup(list
[ii
]));
6618 chanserv_conf
.set_shows
= strlist
;
6619 /* We don't look things up now, in case the list refers to options
6620 * defined by modules initialized after this point. Just mark the
6621 * function list as invalid, so it will be initialized.
6623 set_shows_list
.used
= 0;
6624 free_string_list(chanserv_conf
.eightball
);
6625 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6628 strlist
= string_list_copy(strlist
);
6632 strlist
= alloc_string_list(4);
6633 string_list_append(strlist
, strdup("Yes."));
6634 string_list_append(strlist
, strdup("No."));
6635 string_list_append(strlist
, strdup("Maybe so."));
6637 chanserv_conf
.eightball
= strlist
;
6638 free_string_list(chanserv_conf
.old_ban_names
);
6639 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6641 strlist
= string_list_copy(strlist
);
6643 strlist
= alloc_string_list(2);
6644 chanserv_conf
.old_ban_names
= strlist
;
6645 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6646 off_channel
= str
? atoi(str
) : 0;
6650 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6653 struct note_type
*ntype
;
6656 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6658 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6661 if(!(ntype
= chanserv_create_note_type(key
)))
6663 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6667 /* Figure out set access */
6668 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6670 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6671 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6673 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6675 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6676 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6678 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6680 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6684 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6685 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6686 ntype
->set_access
.min_opserv
= 0;
6689 /* Figure out visibility */
6690 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6691 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6692 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6693 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6694 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6695 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6696 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6697 ntype
->visible_type
= NOTE_VIS_ALL
;
6699 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6701 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6702 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6706 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6708 struct handle_info
*handle
;
6709 struct userData
*uData
;
6710 char *seen
, *inf
, *flags
;
6712 unsigned short access
;
6714 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6716 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6720 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6721 if(access
> UL_OWNER
)
6723 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6727 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
6728 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
6729 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
6730 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
6731 handle
= get_handle_info(key
);
6734 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
6738 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
6739 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
6743 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6745 struct banData
*bData
;
6746 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
6747 time_t set_time
, triggered_time
, expires_time
;
6749 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6751 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
6755 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
6756 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
6757 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
6758 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
6759 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
6760 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
6761 if (!reason
|| !owner
)
6764 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
6765 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
6767 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
6769 expires_time
= set_time
+ atoi(s_duration
);
6773 if(!reason
|| (expires_time
&& (expires_time
< now
)))
6776 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
6779 static struct suspended
*
6780 chanserv_read_suspended(dict_t obj
)
6782 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
6786 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
6787 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6788 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
6789 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6790 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
6791 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6792 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
6793 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
6794 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
6795 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
6800 chanserv_channel_read(const char *key
, struct record_data
*hir
)
6802 struct suspended
*suspended
;
6803 struct mod_chanmode
*modes
;
6804 struct chanNode
*cNode
;
6805 struct chanData
*cData
;
6806 struct dict
*channel
, *obj
;
6807 char *str
, *argv
[10];
6811 channel
= hir
->d
.object
;
6813 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
6816 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
6819 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
6822 cData
= register_channel(cNode
, str
);
6825 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
6829 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
6831 enum levelOption lvlOpt
;
6832 enum charOption chOpt
;
6834 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
6835 cData
->flags
= atoi(str
);
6837 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6839 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
6841 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
6842 else if(levelOptions
[lvlOpt
].old_flag
)
6844 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6845 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
6847 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6851 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6853 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
6855 cData
->chOpts
[chOpt
] = str
[0];
6858 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
6860 enum levelOption lvlOpt
;
6861 enum charOption chOpt
;
6864 cData
->flags
= base64toint(str
, 5);
6865 count
= strlen(str
+= 5);
6866 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6869 if(levelOptions
[lvlOpt
].old_flag
)
6871 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6872 lvl
= levelOptions
[lvlOpt
].flag_value
;
6874 lvl
= levelOptions
[lvlOpt
].default_value
;
6876 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
6878 case 'c': lvl
= UL_COOWNER
; break;
6879 case 'm': lvl
= UL_MANAGER
; break;
6880 case 'n': lvl
= UL_OWNER
+1; break;
6881 case 'o': lvl
= UL_OP
; break;
6882 case 'p': lvl
= UL_PEON
; break;
6883 case 'h': lvl
= UL_HALFOP
; break;
6884 case 'w': lvl
= UL_OWNER
; break;
6885 default: lvl
= 0; break;
6887 cData
->lvlOpts
[lvlOpt
] = lvl
;
6889 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6890 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
6893 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
6895 suspended
= chanserv_read_suspended(obj
);
6896 cData
->suspended
= suspended
;
6897 suspended
->cData
= cData
;
6898 /* We could use suspended->expires and suspended->revoked to
6899 * set the CHANNEL_SUSPENDED flag, but we don't. */
6901 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
6903 suspended
= calloc(1, sizeof(*suspended
));
6904 suspended
->issued
= 0;
6905 suspended
->revoked
= 0;
6906 suspended
->suspender
= strdup(str
);
6907 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
6908 suspended
->expires
= str
? atoi(str
) : 0;
6909 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
6910 suspended
->reason
= strdup(str
? str
: "No reason");
6911 suspended
->previous
= NULL
;
6912 cData
->suspended
= suspended
;
6913 suspended
->cData
= cData
;
6917 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6918 suspended
= NULL
; /* to squelch a warning */
6921 if(IsSuspended(cData
)) {
6922 if(suspended
->expires
> now
)
6923 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6924 else if(suspended
->expires
)
6925 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6928 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
6929 struct mod_chanmode change
;
6930 mod_chanmode_init(&change
);
6932 change
.args
[0].mode
= MODE_CHANOP
;
6933 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
6934 mod_chanmode_announce(chanserv
, cNode
, &change
);
6937 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
6938 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6939 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
6940 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6941 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
6942 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6943 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
6944 cData
->max
= str
? atoi(str
) : 0;
6945 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
6946 cData
->greeting
= str
? strdup(str
) : NULL
;
6947 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
6948 cData
->user_greeting
= str
? strdup(str
) : NULL
;
6949 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
6950 cData
->topic_mask
= str
? strdup(str
) : NULL
;
6951 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
6952 cData
->topic
= str
? strdup(str
) : NULL
;
6954 if(!IsSuspended(cData
)
6955 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
6956 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
6957 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
6958 cData
->modes
= *modes
;
6960 cData
->modes
.modes_set
|= MODE_REGISTERED
;
6961 if(cData
->modes
.argc
> 1)
6962 cData
->modes
.argc
= 1;
6963 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
6964 mod_chanmode_free(modes
);
6967 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
6968 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6969 user_read_helper(iter_key(it
), iter_data(it
), cData
);
6971 if(!cData
->users
&& !IsProtected(cData
))
6973 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
6974 unregister_channel(cData
, "has empty user list.");
6978 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
6979 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6980 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
6982 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
6983 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6985 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
6986 struct record_data
*rd
= iter_data(it
);
6987 const char *note
, *setter
;
6989 if(rd
->type
!= RECDB_OBJECT
)
6991 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
6995 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
6997 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
6999 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7003 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7004 if(!setter
) setter
= "<unknown>";
7005 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7013 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7015 const char *setter
, *reason
, *str
;
7016 struct do_not_register
*dnr
;
7018 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7021 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7024 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7027 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7030 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7033 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7035 dnr
->set
= atoi(str
);
7041 chanserv_saxdb_read(struct dict
*database
)
7043 struct dict
*section
;
7046 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7047 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7048 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7050 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7051 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7052 chanserv_channel_read(iter_key(it
), iter_data(it
));
7054 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7055 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7056 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7062 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7064 int high_present
= 0;
7065 saxdb_start_record(ctx
, KEY_USERS
, 1);
7066 for(; uData
; uData
= uData
->next
)
7068 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7070 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7071 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7072 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7074 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7076 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7077 saxdb_end_record(ctx
);
7079 saxdb_end_record(ctx
);
7080 return high_present
;
7084 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7088 saxdb_start_record(ctx
, KEY_BANS
, 1);
7089 for(; bData
; bData
= bData
->next
)
7091 saxdb_start_record(ctx
, bData
->mask
, 0);
7092 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7093 if(bData
->triggered
)
7094 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7096 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7098 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7100 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7101 saxdb_end_record(ctx
);
7103 saxdb_end_record(ctx
);
7107 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7109 saxdb_start_record(ctx
, name
, 0);
7110 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7111 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7113 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7115 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7117 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7119 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7120 saxdb_end_record(ctx
);
7124 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7128 enum levelOption lvlOpt
;
7129 enum charOption chOpt
;
7131 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7133 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7134 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7136 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7137 if(channel
->registrar
)
7138 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7139 if(channel
->greeting
)
7140 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7141 if(channel
->user_greeting
)
7142 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7143 if(channel
->topic_mask
)
7144 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7145 if(channel
->suspended
)
7146 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7148 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7149 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7150 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7151 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7152 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7154 buf
[0] = channel
->chOpts
[chOpt
];
7156 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7158 saxdb_end_record(ctx
);
7160 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7162 mod_chanmode_format(&channel
->modes
, buf
);
7163 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7166 high_present
= chanserv_write_users(ctx
, channel
->users
);
7167 chanserv_write_bans(ctx
, channel
->bans
);
7169 if(dict_size(channel
->notes
))
7173 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7174 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7176 struct note
*note
= iter_data(it
);
7177 saxdb_start_record(ctx
, iter_key(it
), 0);
7178 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7179 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7180 saxdb_end_record(ctx
);
7182 saxdb_end_record(ctx
);
7185 if(channel
->ownerTransfer
)
7186 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7187 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7188 saxdb_end_record(ctx
);
7192 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7196 saxdb_start_record(ctx
, ntype
->name
, 0);
7197 switch(ntype
->set_access_type
)
7199 case NOTE_SET_CHANNEL_ACCESS
:
7200 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7202 case NOTE_SET_CHANNEL_SETTER
:
7203 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7205 case NOTE_SET_PRIVILEGED
: default:
7206 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7209 switch(ntype
->visible_type
)
7211 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7212 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7213 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7215 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7216 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7217 saxdb_end_record(ctx
);
7221 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7223 struct do_not_register
*dnr
;
7226 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7228 dnr
= iter_data(it
);
7229 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7231 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7232 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7233 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7234 saxdb_end_record(ctx
);
7239 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7242 struct chanData
*channel
;
7245 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7246 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7247 chanserv_write_note_type(ctx
, iter_data(it
));
7248 saxdb_end_record(ctx
);
7251 saxdb_start_record(ctx
, KEY_DNR
, 1);
7252 write_dnrs_helper(ctx
, handle_dnrs
);
7253 write_dnrs_helper(ctx
, plain_dnrs
);
7254 write_dnrs_helper(ctx
, mask_dnrs
);
7255 saxdb_end_record(ctx
);
7258 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7259 for(channel
= channelList
; channel
; channel
= channel
->next
)
7260 chanserv_write_channel(ctx
, channel
);
7261 saxdb_end_record(ctx
);
7267 chanserv_db_cleanup(void) {
7269 unreg_part_func(handle_part
);
7271 unregister_channel(channelList
, "terminating.");
7272 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7273 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7274 free(chanserv_conf
.support_channels
.list
);
7275 dict_delete(handle_dnrs
);
7276 dict_delete(plain_dnrs
);
7277 dict_delete(mask_dnrs
);
7278 dict_delete(note_types
);
7279 free_string_list(chanserv_conf
.eightball
);
7280 free_string_list(chanserv_conf
.old_ban_names
);
7281 free_string_list(chanserv_conf
.set_shows
);
7282 free(set_shows_list
.list
);
7283 free(uset_shows_list
.list
);
7286 struct userData
*helper
= helperList
;
7287 helperList
= helperList
->next
;
7292 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7293 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7294 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7297 init_chanserv(const char *nick
)
7299 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7300 conf_register_reload(chanserv_conf_read
);
7302 reg_server_link_func(handle_server_link
);
7304 reg_new_channel_func(handle_new_channel
);
7305 reg_join_func(handle_join
);
7306 reg_part_func(handle_part
);
7307 reg_kick_func(handle_kick
);
7308 reg_topic_func(handle_topic
);
7309 reg_mode_change_func(handle_mode
);
7310 reg_nick_change_func(handle_nick_change
);
7312 reg_auth_func(handle_auth
);
7313 reg_handle_rename_func(handle_rename
);
7314 reg_unreg_func(handle_unreg
);
7316 handle_dnrs
= dict_new();
7317 dict_set_free_data(handle_dnrs
, free
);
7318 plain_dnrs
= dict_new();
7319 dict_set_free_data(plain_dnrs
, free
);
7320 mask_dnrs
= dict_new();
7321 dict_set_free_data(mask_dnrs
, free
);
7323 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7324 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7325 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7326 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7327 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7328 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7329 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7330 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7331 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7332 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7334 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7335 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7337 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7338 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7339 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7340 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7341 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7343 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7344 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7345 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7346 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7347 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7348 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7350 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7351 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7352 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7353 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7355 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7356 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7357 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7358 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7359 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7360 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7361 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7362 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7363 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7364 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7366 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7367 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7368 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7369 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7370 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7371 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7372 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7373 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7374 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7375 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7376 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7377 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7378 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7379 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7381 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7382 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7383 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7384 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7385 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7387 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7388 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7390 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7391 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7392 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7393 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7394 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7395 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7396 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7397 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7398 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7399 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7400 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7401 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7403 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7404 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7406 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7407 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7408 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7409 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7411 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7412 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7413 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7414 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7415 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7417 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7418 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7419 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7420 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7421 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7422 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7423 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7425 /* Channel options */
7426 DEFINE_CHANNEL_OPTION(defaulttopic
);
7427 DEFINE_CHANNEL_OPTION(topicmask
);
7428 DEFINE_CHANNEL_OPTION(greeting
);
7429 DEFINE_CHANNEL_OPTION(usergreeting
);
7430 DEFINE_CHANNEL_OPTION(modes
);
7431 DEFINE_CHANNEL_OPTION(enfops
);
7432 DEFINE_CHANNEL_OPTION(enfhalfops
);
7433 DEFINE_CHANNEL_OPTION(giveops
);
7434 DEFINE_CHANNEL_OPTION(givehalfops
);
7435 DEFINE_CHANNEL_OPTION(protect
);
7436 DEFINE_CHANNEL_OPTION(enfmodes
);
7437 DEFINE_CHANNEL_OPTION(enftopic
);
7438 DEFINE_CHANNEL_OPTION(pubcmd
);
7439 DEFINE_CHANNEL_OPTION(givevoice
);
7440 DEFINE_CHANNEL_OPTION(userinfo
);
7441 DEFINE_CHANNEL_OPTION(dynlimit
);
7442 DEFINE_CHANNEL_OPTION(topicsnarf
);
7443 DEFINE_CHANNEL_OPTION(nodelete
);
7444 DEFINE_CHANNEL_OPTION(toys
);
7445 DEFINE_CHANNEL_OPTION(setters
);
7446 DEFINE_CHANNEL_OPTION(topicrefresh
);
7447 DEFINE_CHANNEL_OPTION(ctcpusers
);
7448 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7449 DEFINE_CHANNEL_OPTION(inviteme
);
7451 DEFINE_CHANNEL_OPTION(offchannel
);
7452 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7454 /* Alias set topic to set defaulttopic for compatibility. */
7455 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7458 DEFINE_USER_OPTION(noautoop
);
7459 DEFINE_USER_OPTION(autoinvite
);
7460 DEFINE_USER_OPTION(info
);
7462 /* Alias uset autovoice to uset autoop. */
7463 modcmd_register(chanserv_module
, "uset noautovoice", user_opt_noautoop
, 1, 0, NULL
);
7465 note_types
= dict_new();
7466 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7469 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7470 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7471 service_register(chanserv
)->trigger
= '!';
7472 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7474 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7476 if(chanserv_conf
.channel_expire_frequency
)
7477 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7479 if(chanserv_conf
.refresh_period
)
7481 time_t next_refresh
;
7482 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7483 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7486 reg_exit_func(chanserv_db_cleanup
);
7487 message_register_table(msgtab
);